Railway Operation Simulator  v2.8.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
63  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
64  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
65  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
66  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
67  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
68 /*
69  Construct a new train with general default values and input values for position and headcode.
70  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
71  This is because trains are kept in a vector and vectors erase elements during internal operations.
72  Deletion is explicit by using a special function. Increment the static class member NextTrainID
73  after setting this train's ID.
74 */
75 
76 {
77  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
78  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
79  AnsiString(TrainModeIn));
80  // AutoControl = true;//all trains start in auto control
81  UpdateCounter = 0;
82  TimeTimeLocArrived = false;
83  Derailed = false;
84  DerailPending = false;
85  Crashed = false;
86  StoppedAtBuffers = false;
87  StoppedAtSignal = false;
88  StoppedAtLocation = false;
89  StoppedAfterSPAD = false;
90  StoppedWithoutPower = false; // new at v2.4.0
91  StoppedForTrainInFront = false;
92  SignallerStoppingFlag = false;
93  SignallerStopped = false;
94  SignallerRemoved = false;
95  NotInService = false;
96  HoldAtLocationInTTMode = false;
97  AllowedToPassRedSignal = false;
98  CallingOnFlag = false;
99  BeingCalledOn = false;
100  DepartureTimeSet = false;
102  TimetableFinished = false;
103  LastActionDelayFlag = false;
104  OneLengthAccelDecel = false;
105  TrainCrashedInto = -1;
107  Plotted = false;
108  TrainGone = false;
109  SPADFlag = false;
110  FrontCodePtr = new Graphics::TBitmap;
111  FrontCodePtr->PixelFormat = pf8bit;
112  FrontCodePtr->Height = 8;
113  FrontCodePtr->Width = 8;
115  FrontCodePtr->Transparent = false;
116  AValue = sqrt(2 * PowerAtRail / Mass);
118  TerminatedMessageSent = false;
119  JoinedOtherTrainFlag = false;
121  StepForwardFlag = false;
123  for(int x = 0; x < 4; x++)
124  {
125  HeadCodeGrPtr[x] = new Graphics::TBitmap;
126  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
127  HeadCodeGrPtr[x]->Height = 8;
128  HeadCodeGrPtr[x]->Width = 8;
130  HeadCodeGrPtr[x]->Transparent = false;
131  }
132  for(int x = 0; x < 4; x++)
133  {
134  BackgroundPtr[x] = new Graphics::TBitmap;
135  BackgroundPtr[x]->PixelFormat = pf8bit;
136  BackgroundPtr[x]->Height = 8;
137  BackgroundPtr[x]->Width = 8;
139  BackgroundPtr[x]->Transparent = false;
140  }
141  for(int x = 0; x < 4; x++)
142  {
144  // set here to ensure have values
145  }
146  for(int x = 0; x < 4; x++)
147  {
148  PlotElement[x] = -1; // marker for not plotted yet
149  }
150  for(int x = 0; x < 3; x++)
151  {
152  OldZoomOutElement[x] = -1; // marker for not plotted yet
153  }
155  NextTrainID++;
156 
157  // new values added to complete initialisation of all TTrain variables
158 
159  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
160  // TrainDataEntryPtr, initialise in AddTrain
162  FrontElementLength = 0;
163  EntrySpeed = 0;
164  ExitSpeedHalf = 0;
165  ExitSpeedFull = 0;
166  MaxExitSpeed = 0;
167  BrakeRate = 0;
169  FirstHalfMove = true;
170  EntryTime = 0;
171  ExitTimeHalf = 0;
172  ExitTimeFull = 0;
173  ReleaseTime = 0;
174  TRSTime = 0;
175  LastActionTime = 0;
176  Straddle = MidLag;
177  LeadElement = -1;
178  LeadEntryPos = 0;
179  LeadExitPos = 0;
180  MidElement = -1;
181  MidEntryPos = 0;
182  MidExitPos = 0;
183  LagElement = -1;
184  LagEntryPos = 0;
185  LagExitPos = 0;
186  TrainFailed = false; // added at v2.4.0
187  for(int x = 0; x < 4; x++)
188  {
189  HOffset[x] = 0;
190  VOffset[x] = 0;
191  PlotEntryPos[x] = 0;
192  }
193  OpTimeToAct = 60; // default value, new at v2.2.0
194  MinsDelayed = 0.0; // new at v2.2.0
195  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
196  FinishJoinLogSent = false;
197  // added at v2.4.0 to prevent repeatdly logging the event
200  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
204  ZeroPowerNoCDTMessage = false;
209  TrainFailurePending = false;
210  Utilities->CallLogPop(648);
211 }
212 
213 // ---------------------------------------------------------------------------
214 
215 void TTrain::DeleteTrain(int Caller)
216 /*
217  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
218  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
219  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
220  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
221  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
222  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
223  No need to delete HeadCodePosition as that just points to existing bitmaps
224 */{
225  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
226  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
227  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
228  if(Display->ZoomOutFlag)
229  {
231  }
232  if(FrontCodePtr == 0)
233  {
234  throw Exception("Error in attempting to delete FrontCodePtr");
235  }
236  delete FrontCodePtr;
237  FrontCodePtr = 0;
238  for(int x = 0; x < 4; x++)
239  {
240  if(BackgroundPtr[x] == 0)
241  {
242  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
243  }
244  delete BackgroundPtr[x];
245  BackgroundPtr[x] = 0;
246  }
247  for(int x = 0; x < 4; x++)
248  {
249  if(HeadCodeGrPtr[x] == 0)
250  {
251  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
252  }
253  delete HeadCodeGrPtr[x];
254  HeadCodeGrPtr[x] = 0;
255  }
256  Utilities->CallLogPop(649);
257 }
258 
259 // ---------------------------------------------------------------------------
260 
262 /*
263  Plots the train starting position on screen. Note that the check for starting on straight points &
264  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
265  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
266  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
267  Set the headcode graphics pointers from the headcode text, then check whether starting at a
268  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
269  for the continuation element. Otherwise set Lead and Mid values,
270 
271  and Lead element value unless
272  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
273  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
274  then check if a train on either Mid or Lag and if so give a warning message and return false so
275  that the calling function can delete the train. Plot the Mid element train values then do similarly
276  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
277  the train. Finally set the Plotted flag and return true.
278 */{
279  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
280  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
281 
283  // PlotStartTime = TrainController->TTClockTime;
284  FirstHalfMove = true;
285 
286  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
287  // 'claim' it for this train to prevent any other waiting trains trying to enter
289  {
290  LagElement = -1; // not to be plotted
291  LagExitPos = 0; // not to be plotted
292  LagEntryPos = 0; // not to be plotted
293  MidElement = -1; // not to be plotted
294  MidExitPos = 0; // not to be plotted
295  MidEntryPos = 0; // not to be plotted
297  LeadExitPos = 1; // will be 1 for continuation entry
298  LeadEntryPos = 0;
299 
301  MaxExitSpeed = StartSpeed; // initial value
303  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
304  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
305  if(EntrySpeed > SpeedLimit)
306  {
307  EntrySpeed = SpeedLimit;
308  }
310  {
312  }
314  // LeadElement is the element to be entered
315 
316  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
317  // can achieve ExitSpeedFull at the half braking rate.
319  {
320  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
321  if(TempEntrySpeed < EntrySpeed)
322  {
323  EntrySpeed = TempEntrySpeed;
325  }
326  }
327  Straddle = MidLag; // only for starting on a continuation
329  // no need to stop gap flashing if start on continuation
330  }
331  else // not starting at a continuation
332  {
333  LagElement = -1;
334  LagEntryPos = 0;
335  LagExitPos = 0;
342 
344  MaxExitSpeed = StartSpeed; // initial value
346  bool TempDerail = false; // dummy
347  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
349  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
350  {
351  StoppedWithoutPower = true;
352  }
353  // facing buffers check - ignore starting speed if start facing buffers
354  StoppedAtBuffers = false;
355  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
358  {
359  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
360  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
361  EntrySpeed = 0;
362  ExitSpeedHalf = 0;
363  ExitSpeedFull = 0;
364  MaxExitSpeed = 0;
365  // SetTrainMovementValues not called so set this here
366  BrakeRate = 0;
369  StoppedAtSignal = false;
370  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
371  // signal check is an 'else'
372  if(!StoppedAtLocation)
373  {
374  StoppedAtBuffers = true; // stopped at location takes precedence
375  }
376  }
377 
378  // facing continuation check - don't allow to stop even if no power
380  {
381  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
382  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
386  BrakeRate = 0;
387  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
388  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
389  }
390 
391  // Signal check
392  else if((NextElementPosition > -1) && (NextEntryPos > -1))
393  // condition check added as precaution after SloughIECC error reported by James U
394  {
395  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
396  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
397  {
398  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
399  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
400  EntrySpeed = 0;
401  ExitSpeedHalf = 0;
402  ExitSpeedFull = 0;
403  MaxExitSpeed = 0;
404  BrakeRate = 0;
407  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
408  {
409  StoppedAtSignal = true;
411  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
412  }
414  {
415  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
416  // forwards, but don't change the background colour so still shows as stopped at location
417  StoppedAtSignal = true;
418  }
419  }
420  else
421  {
422  StoppedAtSignal = false;
423  if(NextEntryPos > 1)
424  {
425  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
426  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
427  }
428  else
429  {
430  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
431  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
432  }
433  if(EntrySpeed > SpeedLimit)
434  {
435  EntrySpeed = SpeedLimit;
436  }
438  {
440  }
442  TDateTime TestTime = TrainController->TTClockTime; // test
443  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
444  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
445  // NextElement is the element to be entered
446 
447  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
448  // can achieve ExitSpeedFull at the half braking rate.
450  {
451  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
452  // half braking
453  if(TempEntrySpeed < EntrySpeed)
454  {
455  EntrySpeed = TempEntrySpeed;
456  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
457  }
458  }
459  }
460  }
462  {
463  throw Exception("Error, LeadElement Exit Connection is NotSet");
464  }
465  }
466  if(MidElement > -1) // will be -1 if start on continuation
467  {
468  Straddle = LeadMid;
472  {
473  for(int x = 0; x < 4; x++)
474  {
475  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
476  }
477  }
478  else
479  {
480  for(int x = 0; x < 4; x++)
481  {
483  }
484  }
485  if(TrainMode == Timetable)
486  {
488  }
489  else
490  {
492  }
494  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
495 
498 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
499  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
500  {
501  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
502  Utilities->CallLogPop(651);
503  return false;
504  }
505 */
510  PlotTrainGraphic(8, 0, Display);
511  PlotTrainGraphic(9, 1, Display);
512 
515 
516  // pick up background bitmaps [2] & [3]
517 
520 
521  PlotElement[2] = MidElement;
523  PlotElement[3] = MidElement;
525  PlotTrainGraphic(10, 2, Display);
526  PlotTrainGraphic(11, 3, Display);
527  // Plotted = true; set in PlotTrainGraphic
528  }
529  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
530  Utilities->CallLogPop(652);
531 }
532 
533 // ---------------------------------------------------------------------------
534 void TTrain::UnplotTrain(int Caller)
535 {
536  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
537  if(!Plotted)
538  {
539  return;
540  }
541  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
542 
543  if(Straddle == MidLag)
544  {
545  if(MidElement > -1)
546  {
551  // to force plot of locked route marker, needed once only for the element
552  }
553  if(LagElement > -1)
554  {
559  // to force plot of locked route marker, needed once only for the element
560  }
561  }
562  else if(Straddle == LeadMidLag)
563  {
564  if(LeadElement > -1)
565  {
568  // to force plot of locked route marker, needed once only for the element
569  }
570  if(MidElement > -1)
571  {
576  // to force plot of locked route marker, needed once only for the element
577  }
578  if(LagElement > -1)
579  {
582  // to force plot of locked route marker, needed once only for the element
583  }
584  }
585  else if(Straddle == LeadMid)
586  {
587  if(LeadElement > -1)
588  {
593  // to force plot of locked route marker, needed once only for the element
594  }
595  if(MidElement > -1)
596  {
601  // to force plot of locked route marker, needed once only for the element
602  }
603  }
604  if(LeadElement > -1)
605  {
607  }
608  if(MidElement > -1)
609  {
611  }
612  if(LagElement > -1)
613  {
615  }
616  Plotted = false;
618  Display->Update();
619  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
620  // resurrected when Update() dropped from PlotOutput etc
621  Utilities->CallLogPop(653);
622 }
623 
624 // ----------------------------------------------------------------------------
625 
626 void TTrain::UpdateTrain(int Caller)
627 /*
628  Note: Some changes made since comments written
629 
630  Brief:
631  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
632  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
633  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
634  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
635  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
636  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
637  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
638  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
639  changed to MidLag within the function and all elements moved down one, old Mid becomes
640  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
641  incremented to reflect the position the train now occupies.
642 
643  Detail:
644  Set TrainFailurePending if all conditions met
645  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
646  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
647  and return.
648  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
649  If there's a LagElement (there will be but include check for good practice - next
650  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
651  train fully on offending point - Derail set and DerailPanding reset, train background
652  colour changed (note that BackgroundColour is a property of the train itself) then return.
653  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
654  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
655  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
656  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
657  if LeadElement is a fouled trailing point.
658  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
659  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
660  replotting the last background segment and checking whether the element is a bridge or crossover with the other
661  track in a route, in which case the route colour is replotted.
662  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
663  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
664  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
665  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
666  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
667  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
668  train can be deleted by the calling function, and the function returns.
669  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
670  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
671  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
672  basic red aspect.
673 
674  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
675  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
676  regardless of direction, and with the correct front code colour.
677 
678  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
679  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
680  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
681  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
682  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
683  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
684  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
685 
686  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
687  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
688  changed similarly. The function then returns.
689 
690  If Crashed is not set then Straddle is incremented and the function returns.
691 */
692 
693 {
694  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
695  UpdateCounter++;
696  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
697  if(UpdateCounter >= 100)
698  {
699  UpdateCounter = 0;
700  }
701  int RandRange = (TrainController->MTBFHours * 3600) / 53;
702 
703  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
704  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
705  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
706  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
707  // RandomFailureCounter value is fixed for a full cycle of train updates so this
708  // makes sure there's no bunching of failures as there is for a fixed comparison number
709  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
710  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
711  {
712  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
713  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
714  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
715  // don't fail if:
716  // (a) on a continuation (entering or leaving);
717  // (b) already failed;
718  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
719  // (d) train terminated;
720  // (e) crashed or derailed; or
721  // (f) under signaller control and stopped.
722  {
723  if((random(RandRange)) == 0)
724  // max value for RandRange is over 2x10^9
725  {
726  // here if failure due
727  TrainFailurePending = true;
728  // fail when PlotElements set to proper Lead, Mid & Elements
729  }
730  }
731  }
732 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
733  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
734  {
735  StoppedWithoutPower = true;
736  }
737 */
738  int LockedVectorNumber;
739  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
740  // default values - these needed for route checker below
741  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
742 
744  {
746  }
747  if(Crashed || Derailed)
748  {
750  {
751  PlotTrain(7, Display);
752  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
753  Display->Update();
754  }
755  OpTimeToAct = 0.0;
756  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
757  Utilities->CallLogPop(1017);
758  return; // no further action, user has to remove or work around
759  }
761  {
763  }
765  {
767  }
769  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
770  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
771  // to move & then stop again at the same station
772  {
773  TimeTimeLocArrived = false;
774  }
775  if(!Stopped() && !SPADFlag && !TrainFailed)
776  {
778  }
779  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
780  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
781 /* old version where force a stop at buffers regardless of speed
782  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
783  else StoppedAtBuffers = false;
784 */
785 
786  // new version where crash if run into buffers
787  if(!Crashed)
788  {
789  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
790  {
791  if(ExitSpeedFull > 1)
792  {
793  Crashed = true;
797  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
798  // no need for missed action logs - will be sent when train removed
799  StoppedAtBuffers = false;
800  }
802  // stopped at location & stopped without power take precedence
803  {
804  StoppedAtBuffers = true;
805  }
806  else
807  {
808  StoppedAtBuffers = false;
809  }
810  }
811  else
812  {
813  StoppedAtBuffers = false;
814  }
815  }
816  else
817  {
818  StoppedAtBuffers = false;
819  }
820  // if crashed don't want stopped at buffers set
821 
822  // also crash if run into a level crossing that is changing or has barriers up
823  if(!Crashed)
824  {
825  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
826  {
827  int H = Track->TrackElementAt(873, LeadElement).HLoc;
828  int V = Track->TrackElementAt(874, LeadElement).VLoc;
829  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
830  {
831  Crashed = true;
835  // no need for missed action logs - will be sent when train removed
836  }
837  }
838  }
840  {
842  }
843  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
844  if((TrainMode == Timetable) && StoppedAtLocation && (ActionVectorEntryPtr->Command != "")) //if Command == "" then either TimeLoc or TimeTimeLoc
845  {
846  HoldAtLocationInTTMode = true;
847  }
848  else if(TrainMode == Timetable)
849  {
850  HoldAtLocationInTTMode = false;
851  }
852  // in Signaller mode HoldAtLocationInTTMode not changed
853 
854  // check if departure pending & set times unless already set
855  if(TrainMode == Timetable)
856  {
858  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
859  {
860  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
861  {
863  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
864  {
865  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
866  }
867  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
868  DepartureTimeSet = true;
869  }
870  }
871  }
873  {
874  OpTimeToAct = CalcTimeToAct(0); // called after ReleaseTime set
875  // calculate every 1 sec (in real time, not timetable time) for all trains
876  }
877  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
878  if(TrainMode == Timetable)
879  {
880  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
881  {
882  RemainHereLogNotSent = true;
883  }
885  {
886  // ignore TimeLoc & TTLoc departures
887  // Action logs given in functions
889  LastActionTime + TDateTime(30.0 / 86400)))
890  {
891  if(ActionVectorEntryPtr->Command == "fsp")
892  {
893  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
894  FrontTrainSplit(0);
895  if(TrainFailurePending) // ok, stopped so PlotElements set
896  {
897  TrainHasFailed(0);
898  }
899  Utilities->CallLogPop(2041);
900  return;
901  }
902  else if(ActionVectorEntryPtr->Command == "rsp")
903  {
904  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
905  RearTrainSplit(0);
906  if(TrainFailurePending) // ok, stopped so PlotElements set
907  {
908  TrainHasFailed(1);
909  }
910  Utilities->CallLogPop(2042);
911  return;
912  }
913  else if(ActionVectorEntryPtr->Command == "Fjo")
914  {
915  FinishJoin(0);
916  }
917  else if(ActionVectorEntryPtr->Command == "jbo")
918  {
919  JoinedBy(0);
920  }
921  else if(ActionVectorEntryPtr->Command == "cdt")
922  {
924  }
925  else if(ActionVectorEntryPtr->Command == "Fns")
926  {
927  NewTrainService(0);
928  }
929  else if(ActionVectorEntryPtr->Command == "Frh")
930  {
931  RemainHere(0);
932  }
933  else if(ActionVectorEntryPtr->Command == "Fer")
934  {
935  TimetableFinished = true;
936  }
937  // other aspects of 'Fer' dealt with in TTrain::Update()
938  else if(ActionVectorEntryPtr->Command == "F-nshs")
939  {
941  }
942  else if(ActionVectorEntryPtr->Command == "Frh-sh")
943  {
945  }
946  else if(ActionVectorEntryPtr->Command == "Fns-sh")
947  {
949  }
950 /*
951  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
952  shuttle headcode (no train creation)
953  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
954  remain here
955  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
956  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
957 */
958  }
959  }
960  else
961  {
963  {
965  }
966  }
967  }
968  if(TrainMode == Timetable)
969  {
970  if(StoppedAtBuffers)
971  {
972  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
973  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
974  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
975  if(BufferLocation == "")
976  {
978  }
979  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
980  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
981  {
985  {
987  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
988  // Drop missed actions so user can still use sig mode to get back on track
990  }
991  if(TrainFailurePending) // ok, stopped so PlotElements set
992  {
994  TrainHasFailed(2);
995  }
996  Utilities->CallLogPop(1020);
997  return;
998  }
999  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !StoppedAtLocation && (TrainController->TTClockTime >
1000  ReleaseTime))
1001  {
1004  {
1007  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1008  // Drop missed actions so user can still use sig mode to get back on track
1010  }
1011  if(TrainFailurePending) // ok, stopped so PlotElements set
1012  {
1014  TrainHasFailed(3);
1015  }
1016  Utilities->CallLogPop(1397);
1017  return;
1018  }
1019  }
1020  else
1021  {
1023  }
1024  }
1025  else
1026  {
1028  }
1029  if(TrainMode == Timetable)
1030  {
1032  {
1034  }
1036  {
1038  }
1039  }
1040  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1041  // restart after stopped for train in front
1042  int NextElementPosition, NextEntryPos;
1043 
1044  if(LeadElement > -1) // if an exit continuation then not set
1045  {
1046  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1047  {
1049  }
1050  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1051  {
1052  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1053  {
1054  LeadExitPos = 1;
1055  }
1056  else
1057  {
1058  LeadExitPos = 3;
1059  }
1060  }
1061  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1062  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1063  }
1064  else
1065  {
1066  NextElementPosition = -1;
1067  NextEntryPos = -1;
1068  }
1069  if((NextElementPosition > -1) && (NextEntryPos > -1))
1070  // may be buffers or continuation so need this check
1071  {
1072 /*
1073  Check whether calling-on conditions met:-
1074  a) approaching train has stopped at a signal but not at a location;
1075  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1076  change of direction (cdt), remaining here (Frh), or under signaller control);
1077  c) at least 1 platform available for the approaching train;
1078  d) points (if any) set for direct route into platform;
1079  e) approaching train is to stop at station;
1080  f) no more facing signals between train and platform;
1081  g) [dropped g]
1082  h) train in front preventing route being set far enough to release stop signal;
1083  i) train in front not exiting at continuation;
1084  j) signal must be within 4km of the stop platform;
1085  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily]; and
1086  l) no existing route conflicts with the route into the platform.
1087  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1088  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1089 */
1090  if(TrainMode == Timetable)
1091  {
1092  if(CallingOnAllowed(0))
1093  {
1094  CallingOnFlag = true;
1095  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1096  }
1097  else
1098  {
1099  if(CallingOnFlag)
1100  {
1102  }
1103  CallingOnFlag = false;
1104  }
1105  }
1106  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !StoppedAtLocation)
1107  {
1108  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1109  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1110  // sets StoppedAtSignal again & train doesn't move
1111  StoppedAtSignal = false;
1112  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1113  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1114  // LeadMidLag and front of train was on LeadElement (after the current move)
1116  EntrySpeed = 0;
1118  FirstHalfMove = true;
1119  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1120  // NextElement is the element to be entered
1121  }
1123  {
1124  if(ClearToNextSignal(0))
1125  {
1126  StoppedForTrainInFront = false;
1127  BeingCalledOn = false;
1128  EntrySpeed = 0;
1130  FirstHalfMove = true;
1131  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1132  }
1133  else
1134  {
1135  if(TrainFailurePending) // ok, stopped so PlotElements set
1136  {
1137  TrainHasFailed(4);
1138  }
1139  Utilities->CallLogPop(1097);
1140  return;
1141  }
1142  }
1143  }
1144  if((Straddle == MidLag) && (LeadElement != -1))
1145  // later check only for Straddle == LeadMid, so need this check here for initial train start
1146  {
1148  }
1149 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1150  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1151  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1152  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1153  which could be when start as Snt.
1154  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1155  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1156  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1157  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1158  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1159  reached.
1160  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1161  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1162  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1163  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1164  sending a message to the performancelog.
1165 */
1166 
1167  if(TrainMode == Timetable)
1168  {
1170  {
1171  if(BeingCalledOn)
1172  {
1173  StoppedForTrainInFront = true;
1174  }
1176  {
1178  }
1180  {
1181  // value updated at every scheduled departure & arrival
1183  AnsiString StationName;
1185  {
1187  }
1189  {
1191  }
1192  else
1193  {
1194  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1195  }
1196  EntrySpeed = 0;
1200  FirstHalfMove = true;
1201  StoppedAtLocation = false;
1202 
1203  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1204  {
1205  StoppedWithoutPower = true;
1206  }
1207  if((NextElementPosition > -1) && (NextEntryPos > -1))
1208  // condition check added for SloughIECC error reported by James U
1209  {
1210  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1211  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1212  {
1213  StoppedAtSignal = true;
1214  if(!StoppedWithoutPower)
1215  // if stopped without power just keep existing background colour
1216  {
1218  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1219  }
1220  }
1221  }
1223  {
1224  TimeTimeLocArrived = false;
1225  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1226  // no warning for TimeTimeLoc departure
1227  }
1228  else
1229  {
1231  }
1232  DepartureTimeSet = false;
1233  // no need to set LastActionTime for a departure
1235  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1236  // note
1237 /*
1238  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1239  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1240  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1241  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1242  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1243  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1244  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1245 */
1247  {
1248  StoppedAtBuffers = true;
1249  }
1250  else if(!StoppedWithoutPower)
1251  // if buffers or no power, don't set values
1252  {
1254  {
1255  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1256  // NextElement is the element to be entered
1257  }
1258  else
1259  {
1261  // use LeadElement for an exit continuation
1262  }
1263  }
1264  }
1265  }
1266  }
1267  if(Straddle == LeadMidLag)
1268  {
1270  {
1271  Utilities->CallLogPop(654);
1272  return;
1273  }
1274  }
1275  else
1276  {
1278  {
1279  Utilities->CallLogPop(655);
1280  return;
1281  }
1282  }
1283  if((LeadElement > -1) && (MidElement > -1))
1284  {
1286  {
1287  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1288  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1289  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1290  SignallerStoppingFlag = false;
1291  StepForwardFlag = false;
1292  }
1293  }
1294  if(Stopped())
1295  // this is what prevents another movement if the train is stopped
1296  {
1297  if(TrainFailurePending) // ok, stopped so PlotElements set
1298  {
1299  TrainHasFailed(5);
1300  }
1301  BrakeRate = 0;
1302  Utilities->CallLogPop(656);
1303  return;
1304  }
1305  // here when ready for next move
1306 
1307  // check for train in front & if so stop at next access (when train fully on element next to train)
1308  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1309  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1310  // variable TrainInFrontInSignallerModeFlag
1311  {
1312  if(LeadElement > -1)
1313  {
1314  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1315  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1316  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1317  // true if another train on NextEntryPos track whether bridge or not
1318  {
1319  StoppedForTrainInFront = true;
1320  }
1321  else
1322  {
1323  StoppedForTrainInFront = false;
1324  }
1325  }
1326  }
1327  if((Straddle == LeadMid) && SPADFlag)
1328  // give message + plot background when ready to move half past the signal
1329  {
1330  if(NextElementPosition > -1)
1331  {
1332  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1333  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1334  {
1335  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1337  // if goes past 2 signals then give message twice
1339  }
1340  }
1341  }
1342  if(Straddle == LeadMidLag)
1343  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1344  {
1345  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1346  if(SPADFlag)
1347  {
1348  if(ExitSpeedFull == 0)
1349  {
1350  StoppedAfterSPAD = true;
1351  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1352  }
1353  }
1355  {
1356  if(ExitSpeedFull == 0)
1357  {
1358  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1359  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1360  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1361  // is sent at the right time and once only.
1362  SignallerStopped = true;
1363  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1364  StepForwardFlag = false;
1365  SignallerStoppingFlag = false;
1366  TTrackElement TE;
1367  AnsiString Loc = "";
1368  bool LocNamed = false;
1369  if(LeadElement > -1)
1370  {
1371  TE = Track->TrackElementAt(782, LeadElement);
1372  if(TE.ActiveTrackElementName != "")
1373  {
1374  Loc = TE.ActiveTrackElementName;
1375  LocNamed = true;
1376  }
1377  else
1378  {
1379  Loc = "track element " + TE.ElementID;
1380  }
1381  }
1382  if((MidElement > -1) && !LocNamed)
1383  {
1384  TE = Track->TrackElementAt(783, MidElement);
1385  if(TE.ActiveTrackElementName != "")
1386  {
1387  Loc = TE.ActiveTrackElementName;
1388  LocNamed = true;
1389  }
1390  else if(Loc == "")
1391  {
1392  Loc = "track element " + TE.ElementID;
1393  }
1394  }
1395  if(Loc == "")
1396  {
1397  Loc = "outside railway";
1398  // must have stopped after left at a continuation (because both lead & mid == -1)
1399  }
1400  else
1401  {
1402  Loc = "at " + Loc;
1403  }
1404  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1405  }
1406  }
1407  if(LeadElement > -1) // if an exit continuation then not set
1408  {
1409  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1410  {
1412  }
1413  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1414  {
1415  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1416  {
1417  LeadExitPos = 1;
1418  }
1419  else
1420  {
1421  LeadExitPos = 3;
1422  }
1423  }
1424  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1425  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1426  }
1427  else
1428  {
1429  NextElementPosition = -1;
1430  NextEntryPos = -1;
1431  }
1434  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1435 
1436  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1437  {
1438  StoppedWithoutPower = true;
1439  }
1440  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1441  // may be buffers or continuation. SPADFlag added at v2.1.0
1442  // so don't override the SPAD colour & don't set StoppedAtSignal
1443  {
1444  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1445  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !StoppedAtLocation)
1446  {
1447  StoppedAtSignal = true;
1448  if(!StoppedWithoutPower)
1449  // leave background as is if no power, but set StoppedAtSignal
1450  {
1452  }
1453  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1454  }
1455  }
1456  if(!Stopped())
1457  {
1458  if((NextElementPosition > -1) && (NextEntryPos > -1))
1459  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1460  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1461  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1462  // function for fuller explanation
1463  {
1464  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1465  // NextElement is the element to be entered
1466  }
1467  // follow the continuation exits:-
1468  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1469  {
1471  // Use LeadElement for calcs if lead is a continuation
1472  }
1473  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1474  {
1476  // Use MidElement for calcs if mid is a continuation
1477  }
1478  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1479  {
1481  // Use LagElement for calcs if lag is a continuation
1482  }
1483  }
1484  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1485  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1486  // Trains may not be in a route
1487  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1488  {
1489  // NB if LeadElement == -1 then the above test returns false
1490  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1491  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1492  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1493  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1494  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1495  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1496  FirstPair.second).GetELink() == TempELink))
1497  {
1498  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1499  }
1500  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1501  SecondPair.second).GetELink() == TempELink))
1502  {
1503  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1504  }
1505  }
1506  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1507  // Trains may not be in a route
1508  {
1509  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1510  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1511  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1512  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1513  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1514  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1515  FirstPair.second).GetELink() == TempELink))
1516  {
1517  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1518  }
1519  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1520  SecondPair.second).GetELink() == TempELink))
1521  {
1522  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1523  }
1524  }
1525  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1526  // Trains may not be in a route
1527  {
1528  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1529  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1530  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1531  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1532  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1533  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1534  FirstPair.second).GetELink() == TempELink))
1535  {
1536  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1537  }
1538  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1539  SecondPair.second).GetELink() == TempELink))
1540  {
1541  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1542  }
1543  AllRoutes->CheckMapAndRoutes(8); // test
1544  }
1545  if(LagElement > -1)
1546  // not entering at a continuation so can deal with train leaving the lag element
1547  {
1549  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1550  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1551  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1552 
1553  TPrefDirElement PrefDirElement;
1554  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1555  // as this is a 16x16 graphic
1557  {
1559  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1560  }
1561  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1562  {
1563  int RouteNumber;
1564  TrainGone = true;
1565  // flag to indicate train to be deleted - outside this function
1567  {
1568  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1569  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1570  // calc distance from & inc last signal to exit
1571  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1572  int NewLastElement = 0, NewLastExitPos = 0;
1573  // need above because can't change LastElement & LastExitPos until both new values obtained
1574  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1575  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1576  LastElement).TrackType != Points))
1577  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1578  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1579  // leave CumDistance as it was in these circumstances.
1580  {
1581  if(LastExitPos < 2)
1582  {
1583  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1584  }
1585  else
1586  {
1587  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1588  }
1589  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1590  if(NewLastElement == -1)
1591  // this will catch buffers or any other connection failure
1592  {
1593  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1594  }
1595  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1596  if(NewLastExitPos == -1)
1597  {
1598  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1599  }
1600  LastElement = NewLastElement;
1601  LastExitPos = NewLastExitPos;
1602  }
1603  // if at signal add this in too
1604  if(CumDistance < 1200)
1605  {
1606  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1607  }
1608  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1609  // else use 1200m - CumDistance
1610  int FirstDistance = 0;
1611  if(CumDistance >= 1200)
1612  {
1613  FirstDistance = 100;
1614  }
1615  else
1616  {
1617  FirstDistance = 1200 - CumDistance;
1618  }
1619  if(FirstDistance < 100)
1620  {
1621  FirstDistance = 100; // don't allow < 100
1622  }
1623  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1624  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1625  if(ExitSpeedFull > 20.0)
1626  {
1627  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1628  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1629  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1630  // 4320.0 = 3.6 * 1200, .0 to make it a double
1631  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1632  }
1633  else
1634  {
1635  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1636  ContinuationAutoSigEntry.SecondDelay = 120.0;
1637  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1638  }
1639  ContinuationAutoSigEntry.AccessNumber = 0;
1640  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1642  {
1644  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1645  VectorIT++)
1646  {
1647  if(VectorIT->RouteNumber == RouteNumber)
1648  {
1649  // another train has passed out of same route so erase earlier entry
1650  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1651  break;
1652  }
1653  }
1654  }
1655  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1656  }
1658  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1659  Display->Update();
1660  // need to keep this since Update() not called for PlotSmallOutput as too slow
1661  Utilities->CallLogPop(659);
1662  return;
1663  }
1664  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1665  if(LeadElement > -1)
1666  {
1668  // changed to lead so reset early
1669  {
1670  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1672  // don't plot if zoomed out
1673  if(!Display->ZoomOutFlag)
1674  {
1676  }
1677  // covers signal resetting in same direction
1678  }
1679  }
1681  {
1682  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1683  {
1684  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1685  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1686  TPrefDirElement PrefDirElement;
1687  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1689  {
1691  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1692  }
1694  LockedVectorNumber)))
1695  {
1697  }
1698  }
1699  }
1700  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1701  {
1702  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1704  // don't plot if zoomed out
1705  if(!Display->ZoomOutFlag)
1706  {
1708  }
1709  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1710  }
1712  {
1713  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1714  {
1715  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1716  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1717  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1718  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1719  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1720  int RouteNumber;
1722  // already know it's an autosigsroute, this is just to get the RouteNumber
1723  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1724  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1725  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1726  int RouteNumber2;
1728  // already know it's an autosigsroute, this is just to get the RouteNumber
1729  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1730  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1731  {
1732  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1733  // this was in the 1.3.0 addition but without the condition
1734  }
1735  // end of 1.3.2 addition
1736  // end of 1.3.0.addition
1737  }
1738  TPrefDirElement PrefDirElement;
1739  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1741  {
1743  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1744  }
1745  }
1746  }
1747  }
1748  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1749  if(Straddle == LeadMid)
1750  {
1751  AllowedToPassRedSignal = false;
1752  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1753  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1754  if(DerailPending)
1755  // set during last GetLeadElement, but only act on it when train fully on offending point
1756  // i.e. next time Straddle reaches LeadMid
1757  {
1758  Derailed = true;
1759  DerailPending = false;
1763  Utilities->CallLogPop(657);
1764  return;
1765  }
1772  Straddle = MidLag;
1773  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1774  // LeadElement during this function (note that if stopped at signal then won't get this far)
1775  if(LeadElement > -1)
1776  {
1778  // i.e an exit continuation only
1779  // if don't exclude entry continuations then can't progress past it
1780  {
1781  LeadElement = -1;
1782  }
1783  else
1784  {
1785  GetLeadElement(0);
1786  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1788  if(Stopped())
1789  {
1790  if(TrainFailurePending) // ok, stopped so PlotElements set
1791  {
1792  TrainHasFailed(6);
1793  }
1794  Utilities->CallLogPop(658);
1795  return; // i.e. don't move forward one step if next element is a red signal
1796  }
1797  }
1798  }
1799  }
1800  if(LagElement > -1)
1801  {
1802  // below are the actions required at both half moves for LagElement > -1
1804 
1805  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1806  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1807  // need to do this for each half element
1808 
1809  TPrefDirElement PrefDirElement;
1810  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1811  {
1812  int RouteNumber; // holder for call below - not used
1814  {
1815  if(Utilities->clTransparent == TColor(0xFFFFFF))
1816  // change to black for a white background
1817  {
1819  // only applies for AutoSigs Route in case was locked & timed out
1820  }
1821  else
1822  // change to white for a dark background
1823  {
1825  // only applies for AutoSigs Route in case was locked & timed out
1826  }
1828  }
1829  }
1831  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1832  // or a train on the opposite track - needs to be replotted
1833  }
1834  // update all array values
1835  HOffset[3] = HOffset[2];
1836  HOffset[2] = HOffset[1];
1837  HOffset[1] = HOffset[0];
1838  VOffset[3] = VOffset[2];
1839  VOffset[2] = VOffset[1];
1840  VOffset[1] = VOffset[0];
1841  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1842 
1843  BackgroundPtr[3] = BackgroundPtr[2];
1844  BackgroundPtr[2] = BackgroundPtr[1];
1845  BackgroundPtr[1] = BackgroundPtr[0];
1846  BackgroundPtr[0] = TempPtr;
1847 
1848  // update headcode graphics depending on Lead entry value
1849  if(LeadElement > -1) // if Lead is -1 then stays as is
1850  {
1852  {
1853  for(int x = 0; x < 4; x++)
1854  {
1855  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1856  }
1857  }
1858  else
1859  {
1860  for(int x = 0; x < 4; x++)
1861  {
1863  }
1864  }
1865  }
1866  if(TrainMode == Timetable)
1867  {
1869  }
1870  else
1871  {
1873  }
1875 
1876  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1877  if(LeadElement > -1)
1878  {
1879  if(Straddle == MidLag)
1880  // just about to move half onto the new lead element
1881  {
1883  // pick up new background bitmap [0]
1885  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1886  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1887  // check if own ID for entry at continuation, else crashes into itself!
1888  {
1889  // OK if crossing on a bridge
1890  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1891  if(OtherTrainEntryPos == -1)
1892  {
1893  throw Exception("Error - OtherTrainEntryPos not set");
1894  }
1895  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1896  // LeadEntryPos for rear end crashes
1897  (LeadExitPos == OtherTrainEntryPos))
1898  // LeadExitPos for head-on crashes
1899  {
1901  Crashed = true; // only set if Straddle = MidLag
1902  CallingOnFlag = false;
1903  // in case was set, need to disable call on if call on button had been pressed
1904  }
1905  }
1906  else if(MidElement > -1) // will be -1 for continuation entries
1907  {
1908  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1909  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1910  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1911  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1912  int OtherTrainID = -1;
1913  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1914  {
1915  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1916  {
1917  TrainCrashedInto = OtherTrainID;
1918  Crashed = true; // only set if Straddle = MidLag
1919  CallingOnFlag = false;
1920  // in case was set, need to disable call on if call on button had been pressed
1921  }
1922  }
1923  }
1924  }
1925  else
1926  {
1928  // pick up new background bitmap [0]
1930  }
1931  PlotElement[0] = LeadElement;
1933  PlotTrainGraphic(12, 0, Display);
1934  }
1935  if(MidElement > -1)
1936  {
1937  PlotElement[2] = MidElement;
1939  PlotTrainGraphic(1, 2, Display);
1940  }
1941  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
1942  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
1943  if(Straddle == MidLag)
1944  {
1945  if(MidElement > -1)
1946  {
1947  PlotElement[1] = MidElement;
1949  PlotTrainGraphic(2, 1, Display);
1950  }
1951  if(LagElement > -1)
1952  {
1953  PlotElement[3] = LagElement;
1955  PlotTrainGraphic(3, 3, Display);
1956  }
1957  }
1958  else // Straddle == LeadMidLag
1959  {
1960  if(LeadElement > -1)
1961  {
1962  PlotElement[1] = LeadElement;
1964  PlotTrainGraphic(4, 1, Display);
1965  }
1966  if(MidElement > -1)
1967  {
1968  PlotElement[3] = MidElement;
1970  PlotTrainGraphic(5, 3, Display);
1971  }
1972  }
1973  if(Crashed)
1974  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
1975  {
1980  // in case was set, need to disable call on if call on button had been pressed
1987  Straddle = LeadMidLag;
1988  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
1989  Display->Update();
1990  // resurrected when Update() dropped from PlotOutput etc
1991  Utilities->CallLogPop(660);
1992  return;
1993  }
1994  // deal here with station stops & pass times after all replotting done but before Straddle changed
1995  if(TrainMode == Timetable)
1996  {
1997  if(Straddle == LeadMidLag)
1998  {
1999  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2000  {
2001  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2002  // to point to the location arrival entry - before a change of direction
2003  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2004  bool StopRequired = false;
2005  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2006  if(TTVPos > -1) // -1 if can't find it or if name is ""
2007  {
2008  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2009  // or a station where next element contains a train or a stop signal, if so
2010  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2011  // to test the actual track the train is on since it can't be a platform
2012  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2013  TTrackElement NextTrackElement; // default for now
2014  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2015  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2016  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2017  int NextElementEntryPos = -1;
2018  int NextElementExitPos = -1;
2019  bool TrainOnNextElement = false;
2020  bool StopSignalAtNextElement = false;
2021  if(ForwardConnection)
2022  // if no forward connection can't derive anything from it without errors
2023  {
2024  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2025  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2026  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2027  // this is only for signals so no need to worry about points ambiguity
2028  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2029  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2030  }
2031  // logic here is: if(train@stoplinkpos1 || train@stoplinkpos2 || (forward connection && (train on next element || stop signal at next element)))
2032  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2033  {
2034  if(TTVPos > 0)
2035  {
2037  ActionVectorEntryPtr += TTVPos;
2038  }
2039  if(StopRequired)
2040  {
2041  StoppedAtLocation = true;
2042  StoppedAtSignal = false;
2043  // may have been set earlier at line 925 so need to reset as
2044  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2045  // in zoom out mode
2046  if(!TrainFailed)
2047  {
2049  // pale green
2050  }
2053  {
2054  TimeTimeLocArrived = true;
2055  // used in case of later signaller control, when need to know
2056  // whether had arrived or not, to avoid sending the arrival
2057  // message twice, see TInterface::TimetableControl1Click
2058  }
2059  }
2060  else
2061  {
2063  }
2065  {
2067  }
2068  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2070  }
2071  }
2072  }
2073  }
2074  }
2075  if(Straddle == MidLag)
2076  {
2077  Straddle = LeadMidLag;
2078  FirstHalfMove = false;
2079  }
2080  else if(Straddle == LeadMidLag)
2081  {
2082  Straddle = LeadMid;
2083  FirstHalfMove = true;
2084  }
2085  else if(Straddle == LeadMid)
2086  {
2087  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2088  }
2089  if(TrainFailurePending) // ok, moving but PlotElements set above
2090  {
2091  TrainHasFailed(7);
2092  }
2093  Display->Update();
2094  // need to keep this since Update() not called for PlotSmallOutput as too slow
2095  Utilities->CallLogPop(661);
2096 }
2097 
2098 // ----------------------------------------------------------------------------
2099 
2100 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2101 {
2102  switch(CodeChar)
2103  {
2104  case '0':
2105  return(RailGraphics->Code0);
2106 
2107  case '1':
2108  return(RailGraphics->Code1);
2109 
2110  case '2':
2111  return(RailGraphics->Code2);
2112 
2113  case '3':
2114  return(RailGraphics->Code3);
2115 
2116  case '4':
2117  return(RailGraphics->Code4);
2118 
2119  case '5':
2120  return(RailGraphics->Code5);
2121 
2122  case '6':
2123  return(RailGraphics->Code6);
2124 
2125  case '7':
2126  return(RailGraphics->Code7);
2127 
2128  case '8':
2129  return(RailGraphics->Code8);
2130 
2131  case '9':
2132  return(RailGraphics->Code9);
2133 
2134  case 'A':
2135  return(RailGraphics->CodeA);
2136 
2137  case 'B':
2138  return(RailGraphics->CodeB);
2139 
2140  case 'C':
2141  return(RailGraphics->CodeC);
2142 
2143  case 'D':
2144  return(RailGraphics->CodeD);
2145 
2146  case 'E':
2147  return(RailGraphics->CodeE);
2148 
2149  case 'F':
2150  return(RailGraphics->CodeF);
2151 
2152  case 'G':
2153  return(RailGraphics->CodeG);
2154 
2155  case 'H':
2156  return(RailGraphics->CodeH);
2157 
2158  case 'I':
2159  return(RailGraphics->CodeI);
2160 
2161  case 'J':
2162  return(RailGraphics->CodeJ);
2163 
2164  case 'K':
2165  return(RailGraphics->CodeK);
2166 
2167  case 'L':
2168  return(RailGraphics->CodeL);
2169 
2170  case 'M':
2171  return(RailGraphics->CodeM);
2172 
2173  case 'N':
2174  return(RailGraphics->CodeN);
2175 
2176  case 'O':
2177  return(RailGraphics->CodeO);
2178 
2179  case 'P':
2180  return(RailGraphics->CodeP);
2181 
2182  case 'Q':
2183  return(RailGraphics->CodeQ);
2184 
2185  case 'R':
2186  return(RailGraphics->CodeR);
2187 
2188  case 'S':
2189  return(RailGraphics->CodeS);
2190 
2191  case 'T':
2192  return(RailGraphics->CodeT);
2193 
2194  case 'U':
2195  return(RailGraphics->CodeU);
2196 
2197  case 'V':
2198  return(RailGraphics->CodeV);
2199 
2200  case 'W':
2201  return(RailGraphics->CodeW);
2202 
2203  case 'X':
2204  return(RailGraphics->CodeX);
2205 
2206  case 'Y':
2207  return(RailGraphics->CodeY);
2208 
2209  case 'Z':
2210  return(RailGraphics->CodeZ);
2211 
2212  case 'a':
2213  return(RailGraphics->Code_a);
2214 
2215  case 'b':
2216  return(RailGraphics->Code_b);
2217 
2218  case 'c':
2219  return(RailGraphics->Code_c);
2220 
2221  case 'd':
2222  return(RailGraphics->Code_d);
2223 
2224  case 'e':
2225  return(RailGraphics->Code_e);
2226 
2227  case 'f':
2228  return(RailGraphics->Code_f);
2229 
2230  case 'g':
2231  return(RailGraphics->Code_g);
2232 
2233  case 'h':
2234  return(RailGraphics->Code_h);
2235 
2236  case 'i':
2237  return(RailGraphics->Code_i);
2238 
2239  case 'j':
2240  return(RailGraphics->Code_j);
2241 
2242  case 'k':
2243  return(RailGraphics->Code_k);
2244 
2245  case 'l':
2246  return(RailGraphics->Code_l);
2247 
2248  case 'm':
2249  return(RailGraphics->Code_m);
2250 
2251  case 'n':
2252  return(RailGraphics->Code_n);
2253 
2254  case 'o':
2255  return(RailGraphics->Code_o);
2256 
2257  case 'p':
2258  return(RailGraphics->Code_p);
2259 
2260  case 'q':
2261  return(RailGraphics->Code_q);
2262 
2263  case 'r':
2264  return(RailGraphics->Code_r);
2265 
2266  case 's':
2267  return(RailGraphics->Code_s);
2268 
2269  case 't':
2270  return(RailGraphics->Code_t);
2271 
2272  case 'u':
2273  return(RailGraphics->Code_u);
2274 
2275  case 'v':
2276  return(RailGraphics->Code_v);
2277 
2278  case 'w':
2279  return(RailGraphics->Code_w);
2280 
2281  case 'x':
2282  return(RailGraphics->Code_x);
2283 
2284  case 'y':
2285  return(RailGraphics->Code_y);
2286 
2287  case 'z':
2288  return(RailGraphics->Code_z);
2289 
2290  default:
2291  return(RailGraphics->TempHeadCode);
2292  }
2293 }
2294 
2295 // ----------------------------------------------------------------------------
2296 
2297 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2298 {
2299  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2300  if(Code.Length() != 4)
2301  {
2302  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2303  }
2304  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2305  {
2306  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2307  }
2308  if(BackgroundColour != clB5G5R5)
2309  // i.e. not the basic graphic colour as loaded from resource file
2310  {
2311  for(int x = 0; x < 4; x++)
2312  {
2314  }
2315  }
2316  Utilities->CallLogPop(1484);
2317 }
2318 
2319 // ----------------------------------------------------------------------------
2320 
2321 void TTrain::GetLeadElement(int Caller)
2322 // assumes Mid & Lag already set, sets LeadElement,
2323 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2324 {
2325  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2326  DerailPending = false;
2330  {
2331  // attr 0=straight, - links 0 & 1 (0 = lead)
2332  // attr 1=diverging, - links 2 & 3 (2 = lead)
2333  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2334  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2335 
2336  // if enter at lead, exit at whatever attr set at
2337  // if enter at lag, exit at lead, but set derail wrt attribute
2338  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2339  {
2340  LeadExitPos = 1;
2341  }
2342 
2343  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2344  // best to be on safe side
2345  else if(LeadEntryPos == 0)
2346  {
2347  LeadEntryPos = 2;
2348  LeadExitPos = 3;
2349  }
2350  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2351  {
2352  LeadEntryPos = 0;
2353  LeadExitPos = 1;
2354  }
2355  else if(LeadEntryPos == 2)
2356  {
2357  LeadExitPos = 3;
2358  }
2359 
2360  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2361  {
2362  LeadExitPos = 0;
2363  }
2364  else if(LeadEntryPos == 1)
2365  {
2366  LeadExitPos = 0;
2367  DerailPending = true;
2368  }
2369  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2370  {
2371  LeadExitPos = 0;
2372  DerailPending = true;
2373  }
2374  else if(LeadEntryPos == 3)
2375  {
2376  LeadExitPos = 0;
2377  }
2378  }
2379  else if(LeadEntryPos == 0)
2380  {
2381  LeadExitPos = 1;
2382  }
2383  else if(LeadEntryPos == 1)
2384  {
2385  LeadExitPos = 0;
2386  }
2387  else if(LeadEntryPos == 2)
2388  {
2389  LeadExitPos = 3;
2390  }
2391  else if(LeadEntryPos == 3)
2392  {
2393  LeadExitPos = 2;
2394  }
2395  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2396 /* signal check moved to Update() function
2397  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2398  && (TrackElement.Attribute == 0))//0 = red
2399  {
2400  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2401  }
2402  else
2403  {
2404  StoppedAtSignal = false;
2405  }
2406 */
2407  Utilities->CallLogPop(662);
2408 }
2409 
2410 // ----------------------------------------------------------------------------
2411 
2412 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2413 {
2414  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2415  switch(Link)
2416  {
2417  case 1:
2418  {
2419  HOffset = 0;
2420  VOffset = 0;
2421  break;
2422  }
2423 
2424  case 2:
2425  {
2426  HOffset = 4;
2427  VOffset = 0;
2428  break;
2429  }
2430 
2431  case 3:
2432  {
2433  HOffset = 8;
2434  VOffset = 0;
2435  break;
2436  }
2437 
2438  case 4:
2439  {
2440  HOffset = 0;
2441  VOffset = 4;
2442  break;
2443  }
2444 
2445  case 6:
2446  {
2447  HOffset = 8;
2448  VOffset = 4;
2449  break;
2450  }
2451 
2452  case 7:
2453  {
2454  HOffset = 0;
2455  VOffset = 8;
2456  break;
2457  }
2458 
2459  case 8:
2460  {
2461  HOffset = 4;
2462  VOffset = 8;
2463  break;
2464  }
2465 
2466  case 9:
2467  {
2468  HOffset = 8;
2469  VOffset = 8;
2470  break;
2471  }
2472 
2473  default:
2474  {
2475  throw Exception("Error in GetOffsetValues - Link value wrong");
2476  }
2477  }
2478  Utilities->CallLogPop(674);
2479 }
2480 
2481 // ---------------------------------------------------------------------------
2482 
2483 bool TTrain::LowEntryValue(int EntryLink) const
2484 {
2485 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2486  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2487 */
2488  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2489  {
2490  return(true);
2491  }
2492  else
2493  {
2494  return(false);
2495  }
2496 }
2497 
2498 // ---------------------------------------------------------------------------
2499 
2500 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2501 {
2502  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2503  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2504  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2505  // default values
2506  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2507 
2508  TAllRoutes::TRouteType RouteType;
2509 
2510  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2511 
2512  TRect SourceRect, DestRect;
2513 
2514  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2515  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2516  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2517  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2518 
2519  TempGraphic->PixelFormat = pf8bit;
2520  TempGraphic->Width = 16;
2521  TempGraphic->Height = 16;
2522  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2523 
2524  if(TempElement.TrackType == Points)
2525  {
2526  TempGraphic->Assign(TempElement.GraphicPtr);
2527  TempGraphic->Transparent = true;
2528  TempGraphic->TransparentColor = Utilities->clTransparent;
2529  if(RouteType == TAllRoutes::AutoSigsRoute)
2530  {
2531  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2532  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2533  }
2534  else
2535  {
2536  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2537  }
2538  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2539  }
2540  else if(TempElement.TrackType == GapJump) // plot set gap
2541  {
2542  if(TempElement.SpeedTag == 88)
2543  {
2544  TempGraphic->Assign(RailGraphics->gl88set);
2545  }
2546  else if(TempElement.SpeedTag == 89)
2547  {
2548  TempGraphic->Assign(RailGraphics->gl89set);
2549  }
2550  else if(TempElement.SpeedTag == 90)
2551  {
2552  TempGraphic->Assign(RailGraphics->gl90set);
2553  }
2554  else if(TempElement.SpeedTag == 91)
2555  {
2556  TempGraphic->Assign(RailGraphics->gl91set);
2557  }
2558  else if(TempElement.SpeedTag == 92)
2559  {
2560  TempGraphic->Assign(RailGraphics->gl92set);
2561  }
2562  else if(TempElement.SpeedTag == 93)
2563  {
2564  TempGraphic->Assign(RailGraphics->bm93set);
2565  }
2566  else if(TempElement.SpeedTag == 94)
2567  {
2568  TempGraphic->Assign(RailGraphics->bm94set);
2569  }
2570  else if(TempElement.SpeedTag == 95)
2571  {
2572  TempGraphic->Assign(RailGraphics->gl95set);
2573  }
2574  TempGraphic->Transparent = true;
2575  TempGraphic->TransparentColor = Utilities->clTransparent;
2576  if(RouteType == TAllRoutes::AutoSigsRoute)
2577  {
2578  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2579  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2580  }
2581  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2582  }
2583  // new for version 0.6
2584  else if(TempElement.TrackType == SignalPost)
2585  {
2586  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2587  {
2588  for(int x = 0; x < 40; x++)
2589  {
2590  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2591  // need to stop aspect
2592  {
2593  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2594  break;
2595  }
2596  }
2597  }
2598  else // normal signal
2599  {
2600  TempGraphic->Assign(TempElement.GraphicPtr);
2601  // GraphicPtr set to normal signal in a signal track element
2602  }
2603  TempGraphic->Transparent = true;
2604  TempGraphic->TransparentColor = Utilities->clTransparent;
2605  if(RouteType == TAllRoutes::AutoSigsRoute)
2606  {
2607  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2608  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2609  }
2610  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2611  }
2612  else
2613  {
2614  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2615  // can't name points gaps or signals so 'else' OK
2616  bool FoundFlag;
2617  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2618  if(FoundFlag)
2619  {
2621  {
2622  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2623  TempGraphic->Assign(RailGraphics->bmName);
2624  TempGraphic->Transparent = true;
2625  TempGraphic->TransparentColor = Utilities->clTransparent;
2626  if(RouteType == TAllRoutes::AutoSigsRoute)
2627  {
2628  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2629  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2630  }
2631  else
2632  {
2633  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2634  }
2635  // draw track on top
2636  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2637  }
2638  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2639  {
2640  TempGraphic->Assign(TempElement.GraphicPtr);
2641  TempGraphic->Transparent = true;
2642  TempGraphic->TransparentColor = Utilities->clTransparent;
2643  // note that can't be an AutoSigsRoute
2644  // now overlay the LC central portion
2645  int BDVectorPos = -1; //not used
2646  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2647  {
2648  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2649  }
2650  else
2651  {
2652  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2653  }
2654  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2655  }
2656  else
2657  {
2658  TempGraphic->Assign(TempElement.GraphicPtr);
2659  TempGraphic->Transparent = true;
2660  TempGraphic->TransparentColor = Utilities->clTransparent;
2661  if(RouteType == TAllRoutes::AutoSigsRoute)
2662  {
2663  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2664  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2665  }
2666  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2667  }
2668  }
2669  else
2670  {
2671  TempGraphic->Assign(TempElement.GraphicPtr);
2672  TempGraphic->Transparent = true;
2673  TempGraphic->TransparentColor = Utilities->clTransparent;
2674  if(RouteType == TAllRoutes::AutoSigsRoute)
2675  {
2676  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2677  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2678  }
2679  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2680  }
2681  }
2682  delete TempGraphic;
2683  Utilities->CallLogPop(675);
2684 }
2685 
2686 // ---------------------------------------------------------------------------
2687 
2688 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2689 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2690 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2691 /*
2692 
2693  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2694  {
2695  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2696  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2697  TAllRoutes::TRouteType RouteType;
2698  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2699  // default values
2700  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2701  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2702  TRect SourceRect, DestRect, ScreenSourceRect;
2703  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2704 
2705  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2706  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2707  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2708 
2709  //add text & user graphics if any to *GraphicPtr prior to adding the track
2710  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2711  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2712  int Right = Left + 8;
2713  int Bottom = Top + 8;
2714  ScreenSourceRect.init(Left, Top, Right, Bottom);
2715  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2716  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2717 
2718  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2719  TempGraphic->PixelFormat = pf8bit;
2720  TempGraphic->Width = 16;
2721  TempGraphic->Height = 16;
2722 
2723  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2724  SourceGraphic->PixelFormat = pf8bit;
2725  SourceGraphic->Width = 16;
2726  SourceGraphic->Height = 16;
2727  SourceGraphic->Transparent = true;
2728  SourceGraphic->TransparentColor = Utilities->clTransparent;
2729 
2730  if (TempElement.TrackType == Points)
2731  {
2732  TempGraphic->Assign(TempElement.GraphicPtr);
2733  TempGraphic->Transparent = true;
2734  TempGraphic->TransparentColor = Utilities->clTransparent;
2735  if (RouteType == TAllRoutes::AutoSigsRoute)
2736  {
2737  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2738  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2739  }
2740  else
2741  {
2742  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2743  }
2744  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2745  }
2746  else if (TempElement.TrackType == GapJump) // plot set gap
2747  {
2748  if (TempElement.SpeedTag == 88)
2749  TempGraphic->Assign(RailGraphics->gl88set);
2750  else if (TempElement.SpeedTag == 89)
2751  TempGraphic->Assign(RailGraphics->gl89set);
2752  else if (TempElement.SpeedTag == 90)
2753  TempGraphic->Assign(RailGraphics->gl90set);
2754  else if (TempElement.SpeedTag == 91)
2755  TempGraphic->Assign(RailGraphics->gl91set);
2756  else if (TempElement.SpeedTag == 92)
2757  TempGraphic->Assign(RailGraphics->gl92set);
2758  else if (TempElement.SpeedTag == 93)
2759  TempGraphic->Assign(RailGraphics->bm93set);
2760  else if (TempElement.SpeedTag == 94)
2761  TempGraphic->Assign(RailGraphics->bm94set);
2762  else if (TempElement.SpeedTag == 95)
2763  TempGraphic->Assign(RailGraphics->gl95set);
2764  TempGraphic->Transparent = true;
2765  TempGraphic->TransparentColor = Utilities->clTransparent;
2766  if (RouteType == TAllRoutes::AutoSigsRoute) {
2767  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2768  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2769  }
2770  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2771  }
2772  // new for version 0.6
2773  else if (TempElement.TrackType == SignalPost)
2774  {
2775  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2776  {
2777  for (int x = 0; x < 40; x++)
2778  {
2779  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2780  // need to stop aspect
2781  {
2782  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2783  break;
2784  }
2785  }
2786  }
2787  else // normal signal
2788  {
2789  TempGraphic->Assign(TempElement.GraphicPtr);
2790  // GraphicPtr set to normal signal in a signal track element
2791  }
2792  TempGraphic->Transparent = true;
2793  TempGraphic->TransparentColor = Utilities->clTransparent;
2794  if (RouteType == TAllRoutes::AutoSigsRoute) {
2795  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2796  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2797  }
2798  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2799  }
2800  else {
2801  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2802  // can't name points gaps or signals so 'else' OK
2803  bool FoundFlag;
2804  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2805  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2806  if (FoundFlag)
2807  {
2808  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2809  {
2810  GraphicPtr->Canvas->CopyRect(DestRect,
2811  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2812  TempGraphic->Assign(RailGraphics->bmName);
2813  TempGraphic->Transparent = true;
2814  TempGraphic->TransparentColor = Utilities->clTransparent;
2815  if (RouteType == TAllRoutes::AutoSigsRoute)
2816  {
2817  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2818  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2819  }
2820  else
2821  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2822  // draw track on top
2823  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2824  SourceRect);
2825  }
2826  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2827  TempGraphic->Assign(TempElement.GraphicPtr);
2828  TempGraphic->Transparent = true;
2829  TempGraphic->TransparentColor = Utilities->clTransparent;
2830  // note that can't be an AutoSigsRoute
2831  // now overlay the LC central portion
2832  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2833  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2834  SourceRect);
2835  }
2836  else {
2837  TempGraphic->Assign(TempElement.GraphicPtr);
2838  TempGraphic->Transparent = true;
2839  TempGraphic->TransparentColor = Utilities->clTransparent;
2840  if (RouteType == TAllRoutes::AutoSigsRoute) {
2841  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2842  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2843  }
2844  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2845  SourceRect);
2846  }
2847  }
2848  else {
2849  TempGraphic->Assign(TempElement.GraphicPtr);
2850  TempGraphic->Transparent = true;
2851  TempGraphic->TransparentColor = Utilities->clTransparent;
2852  if (RouteType == TAllRoutes::AutoSigsRoute) {
2853  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2854  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2855  }
2856  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2857  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2858  }
2859  }
2860  delete TempGraphic;
2861  delete SourceGraphic;
2862  Utilities->CallLogPop();
2863  }
2864 */
2865 // ---------------------------------------------------------------------------
2866 
2867 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2868 {
2869  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2870  if(PlotElement[ArrayNumber] == -1)
2871  {
2872  Utilities->CallLogPop(676);
2873  return; // not plotted yet
2874  }
2875  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2876  // set before plot so gap flashing stops first
2877  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2878  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2879  // Only need to set ID for leading element, stays set until train finally leaves the element
2880  Plotted = true;
2881  Utilities->CallLogPop(677);
2882 }
2883 
2884 // ---------------------------------------------------------------------------
2885 
2886 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2887 {
2888  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2889  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2890 }
2891 
2892 // ---------------------------------------------------------------------------
2893 
2894 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2895 {
2896  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2897  HeadCode);
2898  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2899  {
2900  Utilities->CallLogPop(678);
2901  return(true);
2902  }
2903  else
2904  {
2905  Utilities->CallLogPop(679);
2906  return(false);
2907  }
2908 }
2909 
2910 // ---------------------------------------------------------------------------
2911 
2912 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2913 {
2914  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2915  "," + HeadCode);
2916  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2917  {
2918  Utilities->CallLogPop(680);
2919  return(true);
2920  }
2921  else
2922  {
2923  Utilities->CallLogPop(681);
2924  return(false);
2925  }
2926 }
2927 
2928 // ---------------------------------------------------------------------------
2929 
2930 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
2931 // test whether this train on a bridge on trackpos 0 & 1
2932 {
2933  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
2934  HeadCode);
2935  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
2936  {
2937  Utilities->CallLogPop(682);
2938  return(false);
2939  }
2940  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2941  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
2942  {
2943  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2944  {
2945  throw Exception("Error, same train on two different bridge tracks");
2946  }
2947  else
2948  {
2949  Utilities->CallLogPop(684);
2950  return(true);
2951  }
2952  }
2953  else
2954  {
2955  Utilities->CallLogPop(685);
2956  return(false);
2957  }
2958 }
2959 
2960 // ---------------------------------------------------------------------------
2961 
2962 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
2963 // test whether this train on a bridge on trackpos 2 & 3
2964 {
2965  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
2966  HeadCode);
2967  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
2968  {
2969  Utilities->CallLogPop(686);
2970  return(false);
2971  }
2972  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2973  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2974  {
2975  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
2976  Utilities->CallLogPop(687);
2977  return(true);
2978  }
2979  else
2980  {
2981  Utilities->CallLogPop(688);
2982  return(false);
2983  }
2984 }
2985 
2986 // ---------------------------------------------------------------------------
2987 
2988 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
2989 {
2990  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
2991  AnsiString(EntryPos) + "," + HeadCode);
2992  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
2993 
2994  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
2995  if(Track->GapFlashFlag)
2996  {
2998  {
3001  Track->GapFlashFlag = false;
3002  }
3003  }
3004  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3005  {
3006  if(EntryPos == -1)
3007  {
3008  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3009  }
3010  if(EntryPos < 2)
3011  {
3012  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
3013  }
3014  else
3015  {
3016  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
3017  }
3018  }
3019  Utilities->CallLogPop(690);
3020 }
3021 
3022 // ---------------------------------------------------------------------------
3023 
3024 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3025 {
3026  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3027  AnsiString(EntryPos) + "," + HeadCode);
3028  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3029  {
3030  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3031  }
3032  else
3033  {
3034  if(EntryPos == -1)
3035  {
3036  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3037  }
3038  if(EntryPos < 2)
3039  {
3040  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
3041  }
3042  else
3043  {
3044  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
3045  }
3046  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
3047  // i.e. other train on track 2&3
3048  {
3049  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
3050  }
3051  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
3052  // i.e. other train on track 1&2
3053  {
3054  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
3055  }
3056  else
3057  {
3058  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3059  }
3060  }
3061  Utilities->CallLogPop(691);
3062 }
3063 
3064 // ---------------------------------------------------------------------------
3065 
3066 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3067 {
3068  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3069  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3070  int LockedVectorNumber;
3071 
3072  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3073  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3074  {
3075  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3076  Utilities->CallLogPop(692);
3077  return;
3078  }
3079  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3080  // i.e other track is in a marked route
3081  // LinkPos doesn't have to be the entry position for the above check
3082  {
3083  TRect SourceRect, DestRect;
3084  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3085  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3086  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3087  // identify the route element for the other track
3088  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3089  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3090  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3091  int FirstELink, SecondELink = -1;
3092  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3093  // must be at least one
3094  if(RoutePair2.first > -1)
3095  {
3096  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3097  }
3098  TPrefDirElement RouteElement;
3099  // Graphics::TBitmap *RouteGraphic;
3100  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3101  // i.e. other track is in RoutePair2
3102  {
3103  if(SecondELink == -1)
3104  {
3105  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3106  }
3107  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3108  // error if both have same Link number
3109  {
3110  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3111  }
3112  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3113  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3114  }
3115  else // other track is in RoutePair1
3116  {
3117  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3118  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3119  }
3120  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3121  DestGraphic->PixelFormat = pf8bit;
3122  DestGraphic->Width = 8;
3123  DestGraphic->Height = 8;
3124  DestGraphic->Transparent = true;
3125  // has to be transparent or will overwrite the track that the train has just left
3126  DestGraphic->TransparentColor = Utilities->clTransparent;
3127  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3128  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3129  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3130  // plot locked route marker for other route if appropriate
3131  TPrefDirElement PrefDirElement; // holder for next call, unused
3132  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3133  if(StraddleValue == LeadMidLag)
3134  {
3136  PrefDirElement, LockedVectorNumber))
3137  {
3138  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3139  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3140  }
3141  }
3142  delete DestGraphic;
3143  }
3144  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3145  // also can only be a bridge or trains either have already or soon will crash
3146  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3147  {
3148  Utilities->CallLogPop(695);
3149  return;
3150  }
3151  if(ElementEntryPos > 1) // other train is on track 01
3152  {
3153  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3154  {
3156  }
3157  }
3158  else // other train is on track 23
3159  {
3160  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3161  {
3163  }
3164  }
3165  Utilities->CallLogPop(696);
3166 }
3167 
3168 // ---------------------------------------------------------------------------
3169 
3170 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3171 {
3172  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3173  AnsiString(EntryPos) + "," + HeadCode);
3174  int RouteNumber;
3175  bool WrongRoute = false;
3176  TPrefDirElement RouteElement;
3178  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3179 
3180  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3181  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3182  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3183  {
3184  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3185  {
3186  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3187  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3188  {
3189  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3190  {
3191  // don't call for stub end routes
3193  }
3194  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3195  Utilities->CallLogPop(697);
3196  return;
3197  }
3198  }
3199  // also need to check for a route on a crossing diagonal
3200  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3201  int LinkNumber = TrackElement.Link[EntryPos];
3202  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3203  {
3204  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3205  {
3206  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3207  bool LogActionErrorCalled = false;
3208  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3209  if(LinkNumber == 1)
3210  {
3211  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3212  {
3213  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3214  {
3215  // don't call for stub end routes
3217  LogActionErrorCalled = true;
3218  }
3219  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3220  }
3221  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3222  // not else in case have different routes on each diagonal, though shouldn't be possible
3223  {
3224  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3225  {
3226  // don't call for stub end routes
3228  }
3229  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3230  }
3231  }
3232 
3233  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3234  else if(LinkNumber == 3)
3235  {
3236  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3237  {
3238  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3239  {
3240  // don't call for stub end routes
3242  LogActionErrorCalled = true;
3243  }
3244  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3245  }
3246  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3247  // not else in case have different routes on each diagonal, though shouldn't be possible
3248  {
3249  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3250  {
3251  // don't call for stub end routes
3253  }
3254  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3255  }
3256  }
3257 
3258  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3259  else if(LinkNumber == 7)
3260  {
3261  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3262  {
3263  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3264  {
3265  // don't call for stub end routes
3267  LogActionErrorCalled = true;
3268  }
3269  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3270  }
3271  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3272  // not else in case have different routes on each diagonal, though shouldn't be possible
3273  {
3274  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3275  {
3276  // don't call for stub end routes
3278  }
3279  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3280  }
3281  }
3282 
3283  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3284  else if(LinkNumber == 9)
3285  {
3286  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3287  {
3288  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3289  {
3290  // don't call for stub end routes
3292  LogActionErrorCalled = true;
3293  }
3294  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3295  }
3296  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3297  // not else in case have different routes on each diagonal, though shouldn't be possible
3298  {
3299  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3300  {
3301  // don't call for stub end routes
3303  }
3304  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3305  }
3306  }
3307  }
3308  }
3309  Utilities->CallLogPop(698);
3310  return; // no route on other track or no other track
3311  }
3312  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3313  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3314  {
3315  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3316  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new after v2.4.1 for points check - Xeon repoted it 30/05/20. He found that for routes that
3317  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3318  {
3319  if(RouteElement.GetELinkPos() == EntryPos)
3320  {
3321  Utilities->CallLogPop(699);
3322  return; // right direction
3323  }
3324  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3325  {
3326  Utilities->CallLogPop(700);
3327  return; // right direction (points)
3328  }
3329  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3330  {
3331  Utilities->CallLogPop(701);
3332  return; // right direction (points)
3333  }
3334  else if(RouteElement.GetXLinkPos() == EntryPos)
3335  {
3336  WrongRoute = true;
3337  break; // wrong direction
3338  }
3339  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3340  {
3341  WrongRoute = true;
3342  break; // wrong direction
3343  }
3344  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3345  {
3346  WrongRoute = true;
3347  break; // wrong direction
3348  }
3349  }
3350  }
3351  if(!WrongRoute)
3352  {
3353  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3354  }
3355  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3356  {
3357  // don't call for stub end routes
3359  }
3360  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3361  Utilities->CallLogPop(703);
3362 }
3363 
3364 // ---------------------------------------------------------------------------
3365 
3366 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3367 {
3368  if(BackgroundColour == NewBackgroundColour)
3369  {
3370  return; // don't replot if already correct
3371 
3372  }
3373  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3374  bool ColourError = false, ColourError2 = false;
3375 
3376  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3377  if(ColourError)
3378  {
3379  ColourError2 = true;
3380  }
3381  for(int x = 0; x < 4; x++)
3382  {
3383  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3384  if(ColourError)
3385  {
3386  ColourError2 = true;
3387  }
3388  }
3389  if(ColourError2)
3390  {
3392  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3393  }
3394  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3395  // of motion
3396  for(int x = 0; x < 4; x++)
3397  {
3398  PlotTrainGraphic(6, x, Disp);
3399  }
3400  BackgroundColour = NewBackgroundColour;
3401  Display->Update();
3402  // need to keep this since Update() not called for PlotSmallOutput as too slow
3403  Utilities->CallLogPop(704);
3404 }
3405 
3406 // ---------------------------------------------------------------------------
3407 
3408 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3409 /*
3410 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3411 
3412 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3413 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3414 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3415 full-element moves.
3416 
3417 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3418 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3419 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3420 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3421 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3422 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3423 In this case set the brake rate to maximum to stop as soon as possible.
3424 
3425 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3426 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3427 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3428 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3429 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3430 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3431 
3432 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3433 first to see whether buffers or continuation) in turn is examined: first the length of the
3434 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3435 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3436 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3437 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3438 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3439 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3440 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3441 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3442 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3443 siding then again emeregency braking may be necessary and a crash may result.
3444 
3445 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3446 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3447 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3448 buffer, then the train accelerates for half the element and brakes for the other half.
3449 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3450 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3451 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3452 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3453 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3454 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3455 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3456 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3457 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3458 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3459 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3460 
3461 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3462 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3463 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3464 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3465 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3466 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3467 MaxBrakeRate.
3468 
3469 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3470 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3471 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3472 
3473 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3474 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3475 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3476 
3477 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3478 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3479 when Straddle == LeadMidLag
3480 */
3481 {
3482  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3483  AnsiString(EntryPos) + "," + HeadCode);
3484  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3485  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3486  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3487  TrainInFrontInSignallerModeFlag = false;
3488  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3489  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3490  bool SignallerStopRequired = false;
3491 
3493  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3494 
3495  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3496 
3497  OneLengthAccelDecel = false;
3498  BrakeRate = 0;
3499 
3500 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3501  if(CurrentTrackVectorPosition > -1)
3502  {
3503  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3504  {
3505  if((EntryPos == 0) || (EntryPos == 2))
3506  {
3507  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3508  {
3509  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3510  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3511  }
3512  else
3513  {
3514  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3515  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3516  }
3517  }
3518  else if(EntryPos == 1)
3519  {
3520  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3521  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3522  }
3523  else // == 3
3524  {
3525  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3526  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3527  }
3528  }
3529  else
3530  {
3531  if(EntryPos > 1)
3532  {
3533  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3534  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3535  }
3536  else
3537  {
3538  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3539  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3540  }
3541  }
3542  EntryHalfLength = CurrentElementHalfLength;
3543  FrontElementLength = 2 * CurrentElementHalfLength;
3544  }
3545  else
3546  {
3547  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3548  }
3549  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3550  {
3551  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3552  }
3553  // check if zero entry speed with another train directly in front & if so remain stopped
3554  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3555  {
3556  EntrySpeed = 0;
3557  ExitSpeedHalf = 0;
3558  ExitSpeedFull = 0;
3559  MaxExitSpeed = 0;
3560  BrakeRate = 0;
3561  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3562  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3563  StoppedForTrainInFront = true;
3564  Utilities->CallLogPop(705);
3565  return;
3566  }
3567  // new at v2.4.0 - check for stopped and zero power
3568  if((EntrySpeed < 1) && PowerAtRail < 1)
3569  {
3570  EntrySpeed = 0;
3571  ExitSpeedHalf = 0;
3572  ExitSpeedFull = 0;
3573  MaxExitSpeed = 0;
3574  BrakeRate = 0;
3575  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3576  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3577  StoppedWithoutPower = true;
3578  Utilities->CallLogPop(2125);
3579  return;
3580  }
3581 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3582  if(BeingCalledOn)
3583  {
3584  LimitingSpeed = CallOnMaxSpeed;
3585  }
3586  else
3587  {
3588  LimitingSpeed = MaximumSpeedLimit;
3589  }
3590  if(LimitingSpeed > FrontElementSpeedLimit)
3591  {
3592  LimitingSpeed = FrontElementSpeedLimit;
3593  }
3594  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3595  {
3596  LimitingSpeed = MaxRunningSpeed;
3597  }
3598  FrontElementMaxSpeed = LimitingSpeed;
3599 
3600 /*
3601  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3602  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3603  (2) V/3.6 = U/3.6 - FT;
3604  (3) S = UT/3.6 - 0.5FT^2
3605  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3606 
3607  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3608  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3609  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3610  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3611  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3612 
3613  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3614  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3615  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3616 */
3617 
3618 // check if running past a red signal without permission
3619  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3620  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3621  {
3622  SPADFlag = true; // user has to intervene to reset & restart after spad
3623  }
3624  if(!SPADFlag)
3625  {
3626  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3627 
3628  double ExitSpeedAtMaxBraking;
3629  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3630  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3632  {
3633  ExitSpeedAtMaxBraking = 0;
3634  }
3635  else
3636  {
3637  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3638  }
3639  double SpeedToUse;
3640  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added after v2.4.1 because trains entering at a continuation with zero (or very low) speed
3641  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3642  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3643  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3644  {
3645  SpeedToUse = ExitSpeedAtMaxBraking;
3646  }
3647  else
3648  {
3649  SpeedToUse = LimitingSpeed;
3650  }
3651  if(ExitSpeedFull > SpeedToUse)
3652  {
3653  ExitSpeedFull = SpeedToUse;
3654  }
3655  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3656  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3657 
3658  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3659  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3660  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3661 
3662  do
3663  {
3664  RedSignalFlag = false;
3665  BuffersFlag = false;
3666  StationFlag = false;
3667  BuffersOrContinuationNowFlag = false;
3668  ContinuationNextFlag = false;
3669  // have to reset this after the above test
3670  // add current element length to CumulativeLength
3671  CumulativeLength += (2 * CurrentElementHalfLength);
3672  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3673  {
3674  SignallerStopRequired = true;
3675  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3676  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3677  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3678  if(SignallerStopBrakeRate < TempBR)
3679  {
3680  SignallerStopBrakeRate = TempBR;
3681  }
3682  }
3683  // first check for stops within the length of the current element, where don't want any more checks & don't want
3684  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3685  // during the last loop when the NextTrackVectorPosition was the signal.
3686 
3687  // check if current element is a buffer
3688  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3689  {
3690  // no need to add in the length of this element to CumulativeLength as already included
3691  BuffersFlag = true;
3692  }
3693  // check if current element is a station stop
3694  if(TrainMode == Timetable)
3695  {
3696  bool StopRequired = false;
3697  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3698  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3699  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3700  {
3701  // no need to add in the length of element to CumulativeLength
3702  if(StopRequired)
3703  {
3704  StationFlag = true;
3705  }
3706  }
3707  }
3708  else
3709  {
3710  StationFlag = false;
3711  }
3712  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3713  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3714  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3715  {
3716  BuffersOrContinuationNowFlag = true;
3717  }
3718  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3719  {
3720  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3721  {
3722  if((EntryPos == 0) || (EntryPos == 2))
3723  {
3724  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3725  {
3726  ExitPos = 1;
3727  }
3728  else
3729  {
3730  ExitPos = 3;
3731  }
3732  }
3733  else
3734  {
3735  ExitPos = 0;
3736  }
3737  }
3738  else
3739  {
3740  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3741  }
3742  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3743  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3744  if(NextTrackVectorPosition > -1)
3745  {
3746  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3747  // this test & section added at v0.6
3748  {
3749  if((NextEntryPos == 0) || (NextEntryPos == 2))
3750  {
3751  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3752  {
3753  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3754  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3755  }
3756  else
3757  {
3758  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3759  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3760  }
3761  }
3762  else if(NextEntryPos == 1)
3763  {
3764  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3765  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3766  }
3767  else // == 3
3768  {
3769  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3770  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3771  }
3772  }
3773  else
3774  {
3775  if(NextEntryPos > 1)
3776  {
3777  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3778  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3779  }
3780  else
3781  {
3782  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3783  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3784  }
3785  }
3786  }
3787  else
3788  {
3789  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3790  }
3791  // now check for stops, first cover those where don't want to add in length of next element
3792  // check if next element is a red signal - Attr 0,
3793  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3794  // CurrentTrackVectorPosition not NextTrackVectorPosition
3795  bool StopRequired;
3796  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3797  {
3798  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3799  {
3800  // no need to add in the length of element to CumulativeLength
3801  RedSignalFlag = true;
3802  }
3803  // next element is a red signal
3804  }
3805  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3806  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3807  // at least one platform element free
3809  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3810  NextEntryPos, TrainID))
3811  {
3812  // no need to add in the length of element to CumulativeLength
3813  if(StopRequired)
3814  {
3815  StationFlag = true;
3816  }
3817  }
3818  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3819  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3820  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3821  {
3822  // no need to add in the length of element to CumulativeLength
3823  TrainInFrontInSignallerModeFlag = true;
3824  }
3825  // check if next element is a buffer
3826  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3827  {
3828  // need to add in the length of that element to CumulativeLength
3829  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3830  BuffersFlag = true;
3831  }
3832  // check if next element is a station stop
3834  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3835  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3836  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3837  {
3838  // need to add in the length of that element to CumulativeLength if a stop required
3839  if(StopRequired)
3840  {
3841  StationFlag = true;
3842  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3843  }
3844  }
3845  }
3846  //now can decide whether need to stop over CumulativeLength
3847  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3848  {
3849  // have to come to a stop over CumulativeLength
3850  if(CumulativeLength == FrontElementLength)
3851  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3852  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3853  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3854  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3855  // if not calc speed at halfway point & if less than above set half speed to this value;
3856  // use constant acceleration in calculating half time point
3857  {
3858  MaxExitSpeed = 0;
3859  double MaxHalfSpeed;
3860  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3861  // have to halve the element length, & can't be zero or negative so no need to test
3862  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3863  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3864  {
3865  MaxHalfSpeed = FrontElementMaxSpeed;
3866  }
3867  else
3868  {
3869  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3870  }
3871  if(MaxHalfSpeed > (2 * EntrySpeed))
3872  // use 2x to prevent kangarooing at last element when had
3873  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3874  {
3875  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3876  0.333334);
3877  bool HalfSpeedLimited = false;
3878  if(MaxHalfSpeed < ExitSpeedHalf)
3879  {
3880  ExitSpeedHalf = MaxHalfSpeed;
3881  HalfSpeedLimited = true;
3882  }
3883  if(PowerAtRail > 1)
3884  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3885  {
3886  // [km/h/3.6 = m/s]
3887  ExitTimeHalf =
3888  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3889  }
3890  else
3891  {
3892  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3893  }
3894  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3895  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3896  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3897  // by a long braking period
3898  ExitSpeedFull = 0;
3899  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3900  if(TempBrakeRate > MaxBrakeRate)
3901  {
3902  TempBrakeRate = MaxBrakeRate;
3903  }
3904  // shouldn't be but leave in anyway
3905  if(TempBrakeRate > BrakeRate)
3906  {
3907  BrakeRate = TempBrakeRate;
3908  }
3909  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3910  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3911  if(HalfSpeedLimited)
3912  // this is the change referred to above
3913  {
3914  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
3915  ExitTimeHalf = EntryTime + BrakingTime;
3916  ExitTimeFull = ExitTimeHalf + BrakingTime;
3917  }
3918  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
3919  Utilities->CallLogPop(1095);
3920  return;
3921  }
3922  }
3923  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
3924  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
3925  // calc th, tf, sh, & sf
3926  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3927  if(TempBrakeRate > MaxBrakeRate)
3928  {
3929  TempBrakeRate = MaxBrakeRate;
3930  }
3931  if(TempBrakeRate > BrakeRate)
3932  {
3933  BrakeRate = TempBrakeRate;
3934  }
3935  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3936  if(SignallerStopRequired)
3937  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
3938  {
3940  {
3942  // this prevents the brakerate from reducing for a signaller stop
3943  // regardless of other conditions that may change as progress round the loop
3944  }
3945  }
3947  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
3948  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
3949  {
3951  }
3952  int TempMaxExitSpeed;
3953  // calc current value & if less than MaxExitSpeed set that to this
3954  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
3955  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3956  {
3957  MaxExitSpeedAtHalfBraking = 0;
3958  }
3959  else
3960  {
3961  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3962  }
3963  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
3964  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
3965  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3966  {
3967  TempMaxExitSpeed = FrontElementMaxSpeed;
3968  }
3969  else
3970  {
3971  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
3972  }
3973  if(TempMaxExitSpeed < MaxExitSpeed)
3974  {
3975  MaxExitSpeed = TempMaxExitSpeed;
3976  }
3977  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
3978  // Cumulativelength, and Cumulativelength
3979 
3980  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
3981  {
3982  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
3983  if(ExitSpeedHalfSquared < 10)
3984  {
3985  ExitSpeedHalf = 0;
3986  }
3987  else
3988  {
3989  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
3990  }
3991  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
3992  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
3993  if(ExitSpeedFullSquared < 10)
3994  {
3995  ExitSpeedFull = 0;
3996  }
3997  else
3998  {
3999  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4000  }
4001  if((StationFlag) && (CumulativeLength == FrontElementLength))
4002  {
4003  ExitSpeedFull = 0;
4004  // force a stop for station (not for buffers or red signal)
4005  }
4006  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4007  }
4008  // new condition at v2.4.0
4009  else if(PowerAtRail <= 1)
4010  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4011  // avoid using AValue in denominator or have excessively long times
4012  {
4013  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4014  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4015  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4016 
4017  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4018  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4019  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4020  }
4021  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4022  // without the power need above condition or have hours of delay times, above added at v2.4.0
4023  {
4024  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4025  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4026  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4027  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4028  BrakeRate = 0;
4029  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4030  0.333334);
4031  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4032  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4034  // can accelerate continually over the half length
4035  {
4036  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4037  / 86400.0);
4039  // can accelerate continually over the full length
4040  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4041  {
4042  ExitTimeFull =
4043  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4044  }
4045  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4046  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4047  {
4048  // added at v0.6 as a safeguard
4049  if(MaxExitSpeed < EntrySpeed)
4050  {
4052  }
4053  // to prevent DeltaExitTimeToMaxInSecs being negative
4054  if(MaxExitSpeed < 1)
4055  {
4056  MaxExitSpeed = 1;
4057  }
4058  // to prevent divide by zero error
4059  // below as was
4061  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4062  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4063  (1.5 * AValue * AValue);
4064  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4065  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4066  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4067  }
4068  }
4069  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4070  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4071  // second halves of the element
4072  {
4073  // added at v0.6 as a safeguard
4074  if(MaxExitSpeed < EntrySpeed)
4075  {
4077  }
4078  // to prevent DeltaExitTimeToMaxInSecs being negative
4079  if(MaxExitSpeed < 1)
4080  {
4081  MaxExitSpeed = 1; // to prevent divide by zero error
4082  }
4083  // below as was
4085  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4086  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4087  (1.5 * AValue * AValue);
4088  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4089  // remaining distance to half length
4090  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4091  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4093  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4094  }
4095  }
4096  Utilities->CallLogPop(706);
4097  return;
4098  }
4099  else
4100  {
4101  if(!BuffersOrContinuationNowFlag)
4102  {
4103  if(NextSpeedLimit < LimitingSpeed)
4104  {
4105  LimitingSpeed = NextSpeedLimit;
4106  }
4107  }
4108  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4109  int TempMaxExitSpeed;
4110  // calc current value & if less than MaxExitSpeed set that to this
4111  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4112  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4113  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4114  {
4115  MaxExitSpeedAtHalfBraking = 0;
4116  }
4117  else
4118  {
4119  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4120  }
4121  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4122  {
4123  TempMaxExitSpeed = FrontElementMaxSpeed;
4124  }
4125  else
4126  {
4127  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4128  }
4129  if(TempMaxExitSpeed < MaxExitSpeed)
4130  {
4131  MaxExitSpeed = TempMaxExitSpeed;
4132  }
4133  // MaxExitSpeed is an external variable & this can reduce its value
4134  if(EntrySpeed > LimitingSpeed)
4135  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4136  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4137  {
4138  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4139  if(TempBrakeRate > MaxBrakeRate)
4140  {
4141  TempBrakeRate = MaxBrakeRate;
4142  }
4143  // shouldn't be for speedlimits since all known in advance, but include anyway
4144  if(TempBrakeRate > BrakeRate)
4145  {
4146  BrakeRate = TempBrakeRate;
4147  }
4148  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4149  }
4150  }
4151  if(!BuffersOrContinuationNowFlag)
4152  {
4153  CurrentTrackVectorPosition = NextTrackVectorPosition;
4154  EntryPos = NextEntryPos;
4155  CurrentElementHalfLength = NextElementHalfLength;
4156  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4157  {
4158  ContinuationNextFlag = true;
4159  }
4160  }
4161  }
4162  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4164  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4165  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4166  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4167  // stopping distance after it.
4168 
4169  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4170  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4171 
4172  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4173  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4174  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4175  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4176  // too late
4177 
4178  // set final braking or acc'n speed & time values
4179  if(BrakeRate > 0.01)
4180  {
4181  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4182  if(ExitSpeedHalfSquared < 10)
4183  {
4184  ExitSpeedHalf = 0;
4185  }
4186  else
4187  {
4188  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4189  }
4190  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4191  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4192  if(ExitSpeedFullSquared < 10)
4193  {
4194  ExitSpeedFull = 0;
4195  }
4196  else
4197  {
4198  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4199  }
4200  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4201  }
4202  else
4203  {
4204  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4205  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4206  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4207  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4208 
4209  BrakeRate = 0;
4210  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4211  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4212  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4214  {
4215  if(PowerAtRail > 1)
4216  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4217  {
4218  // [km/h/3.6 = m/s]
4219  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4220  / 86400.0);
4221  }
4222  else
4223  {
4224  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4225  }
4227  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4228  {
4229  if(PowerAtRail > 1)
4230  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4231  {
4232  // [km/h/3.6 = m/s]
4233  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4234  / 86400.0);
4235  }
4236  else
4237  {
4238  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4239  }
4240  }
4241  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4242  {
4243  // added at v0.6 as a safeguard
4244  if(MaxExitSpeed < EntrySpeed)
4245  {
4247  }
4248  // to prevent DeltaExitTimeToMaxInSecs being negative
4249  if(MaxExitSpeed < 1)
4250  {
4251  MaxExitSpeed = 1; // to prevent divide by zero error
4252  }
4253  // below as was
4255  double DeltaExitTimeToMaxInSecs;
4256  double DistanceToMax;
4257  if(PowerAtRail > 1) // added at v2.4.0
4258  {
4259  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4260  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4261  (1.5 * AValue * AValue);
4262  }
4263  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4264  {
4265  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4266  // these not really accurate but will be good enough
4267  DistanceToMax = EntryHalfLength;
4268  }
4269  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4270  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4271  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4272  }
4273  }
4274  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4275  {
4276  // added at v0.6 as a safeguard
4277  if(MaxExitSpeed < EntrySpeed)
4278  {
4280  }
4281  // to prevent DeltaExitTimeToMaxInSecs being negative
4282  if(MaxExitSpeed < 1)
4283  {
4284  MaxExitSpeed = 1; // to prevent divide by zero error
4285  }
4286  // below as was
4288  double DeltaExitTimeToMaxInSecs;
4289  double DistanceToMax;
4290  if(PowerAtRail > 1) // added at v2.4.0
4291  {
4292  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4293  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4294  (1.5 * AValue * AValue);
4295  }
4296  else
4297  {
4298  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4299  // these not really accurate but will be good enough
4300  DistanceToMax = EntryHalfLength / 2;
4301  }
4302  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4303  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4304  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4306  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4307  }
4308  }
4309  }
4310 
4311  else // SPADFlag set
4312  {
4314  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4315  if(ExitSpeedHalfSquared < 10)
4316  {
4317  ExitSpeedHalf = 0;
4318  }
4319  else
4320  {
4321  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4322  }
4323  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4324  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4325  if(ExitSpeedFullSquared < 10)
4326  {
4327  ExitSpeedFull = 0;
4328  }
4329  else
4330  {
4331  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4332  }
4333  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4334 
4335  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4336  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4337  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4338  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4339  // will be the stopping speed.
4340  if(ExitSpeedFull > 0)
4341  {
4342  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4343  {
4344  if((EntryPos == 0) || (EntryPos == 2))
4345  {
4346  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4347  {
4348  ExitPos = 1;
4349  }
4350  else
4351  {
4352  ExitPos = 3;
4353  }
4354  }
4355  else
4356  {
4357  ExitPos = 0;
4358  }
4359  }
4360  else
4361  {
4362  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4363  }
4364  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4365  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4366  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4367  {
4368  int NextElementLength;
4369  if(NextEntryPos > 1)
4370  {
4371  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4372  }
4373  else
4374  {
4375  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4376  }
4377  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4378  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4379  {
4380  ExitSpeedFull = 0;
4381  }
4382  }
4383  }
4384  }
4385  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4386  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4387  {
4388  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4389  if(LeadElement > -1)
4390  {
4392  // don't stop on a continuation either entering or leaving
4393  {
4396  MaxExitSpeed = LimitingSpeed;
4397  BrakeRate = 0;
4398  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4399  // assume length is 50m for ease of calc
4400  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4401  Utilities->CallLogPop(2126);
4402  return;
4403  }
4404  }
4405  else if(MidElement > -1)
4406  {
4408  {
4411  MaxExitSpeed = LimitingSpeed;
4412  BrakeRate = 0;
4413  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4414  // assume length is 50m for ease of calc
4415  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4416  Utilities->CallLogPop(2127);
4417  return;
4418  }
4419  }
4420  else if(LagElement > -1)
4421  {
4423  {
4426  MaxExitSpeed = LimitingSpeed;
4427  BrakeRate = 0;
4428  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4429  // assume length is 50m for ease of calc
4430  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4431  Utilities->CallLogPop(2128);
4432  return;
4433  }
4434  }
4435  if(EntrySpeed > 7.5) // keep going for at least another element
4436  {
4437  ExitSpeedHalf = EntrySpeed - 2.5;
4438  ExitSpeedFull = EntrySpeed - 5;
4439  MaxExitSpeed = LimitingSpeed;
4440  BrakeRate = 0;
4441  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4442  // assume length is 50m for ease of calc
4443  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4444  Utilities->CallLogPop(2129);
4445  return;
4446  }
4447  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4448  {
4449  // will appear to have slowed at steady rate
4450  ExitSpeedHalf = 0;
4451  ExitSpeedFull = 0;
4452  MaxExitSpeed = LimitingSpeed;
4453  BrakeRate = 0;
4454  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4455  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4456  StoppedWithoutPower = true;
4457  Utilities->CallLogPop(2130);
4458  return;
4459  }
4460  }
4461  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4462  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4463  Utilities->CallLogPop(707);
4464 }
4465 // ---------------------------------------------------------------------------
4466 /*
4467  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4468  {
4469  int NextExitPos;
4470  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4471  if(TimetableVector.empty()) return false;
4472  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4473  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4474  {
4475  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4476  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4477  {
4478  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4479  }
4480  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4481  {
4482  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4483  }
4484  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4485  NextElement = TempElement;
4486  }
4487  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4488  if(NextElement.TrackType == Buffers) return true;
4489  return false;//shouldn't reach here but include to prevent compiler return warning
4490  }
4491 */
4492 // ---------------------------------------------------------------------------
4493 
4494 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4495 /*
4496  returns the number by which the train ActionVectorEntryPtr needs
4497  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4498  actions when a stop or pass location has been reached before other timetabled actions have been carried out. If can't find it, or Name
4499  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4500  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4501  or pass (false) the location.
4502 */{
4503  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4504  Stop = false;
4505  if(TimetableFinished || (Name == ""))
4506  {
4507  Utilities->CallLogPop(957);
4508  return(-1);
4509  }
4510  // start looking from current pointer position
4511  for(TActionVectorEntry * Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4512  {
4513  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4514  {
4515  break;
4516  }
4517  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4518  {
4519  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4520  {
4521  Stop = true;
4522  Utilities->CallLogPop(960);
4523  return (Ptr - ActionVectorEntryPtr);
4524  }
4525  }
4526  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4527  {
4528  Utilities->CallLogPop(1517);
4529  return (Ptr - ActionVectorEntryPtr);
4530  }
4531  }
4532  Utilities->CallLogPop(959);
4533  return(-1); // not found a valid entry
4534 }
4535 
4536 // ---------------------------------------------------------------------------
4537 
4539 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4540  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4541  Ignores the call-on signal.
4542 */{
4543  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4544  int ReturnVal = 0;
4545 
4546  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4547  {
4548  Track->TrackVector.at(x).TempTrackMarker01 = false;
4549  Track->TrackVector.at(x).TempTrackMarker23 = false;
4550  }
4551  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4552  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4553 
4554  while(true)
4555  {
4556  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4557  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4558  {
4559  ReturnVal = 1;
4560  break;
4561  }
4562  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4563  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4564  {
4565  ReturnVal = 2;
4566  break;
4567  }
4568  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4569  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4570  {
4571  ReturnVal = 3;
4572  break;
4573  }
4574  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388,
4575  CurrentTrackVectorPosition).TrackType == Crossover))
4576  {
4577  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4578  // must be a loop - reached same point as examined earlier
4579  {
4580  ReturnVal = 4;
4581  break;
4582  }
4583  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4584  {
4585  ReturnVal = 4;
4586  break;
4587  }
4588  }
4589  else
4590  {
4591  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526,
4592  CurrentTrackVectorPosition).TempTrackMarker23))
4593  {
4594  ReturnVal = 4;
4595  break;
4596  }
4597  }
4598  if(EntryPos < 2)
4599  {
4600  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4601  }
4602  else
4603  {
4604  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4605  }
4606  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4607  {
4608  if((EntryPos == 0) || (EntryPos == 2))
4609  {
4610  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4611  {
4612  ExitPos = 1;
4613  }
4614  else
4615  {
4616  ExitPos = 3;
4617  }
4618  }
4619  else
4620  {
4621  ExitPos = 0;
4622  }
4623  }
4624  else
4625  {
4626  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4627  }
4628  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4629  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4630  CurrentTrackVectorPosition = NextTrackVectorPosition;
4631  EntryPos = NextEntryPos;
4632  }
4633  if(ReturnVal == 1)
4634  {
4635  Utilities->CallLogPop(708);
4636  return(false);
4637  }
4638  if(ReturnVal == 2)
4639  {
4640  Utilities->CallLogPop(709);
4641  return(true);
4642  }
4643  if(ReturnVal == 3)
4644  {
4645  Utilities->CallLogPop(946);
4646  return(true);
4647  }
4648  if(ReturnVal == 4)
4649  {
4650  Utilities->CallLogPop(947);
4651  return(true);
4652  }
4653  else
4654  {
4655  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4656  }
4657 }
4658 
4659 // ---------------------------------------------------------------------------
4660 
4662 /*
4663  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4664  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4665  change of direction (cdt), remaining here (Frh), or under signaller control);
4666  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4667  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4668  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4669  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] and l) no existing route conflicts with the route into the platform
4670  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4671  change points outside the route or have a route conflict if another route is set.
4672 */{
4673  if(Track->RouteFlashFlag)
4674  {
4675  return(false); // don't want to create a new route from the stop signal if one is already in construction as
4676  }
4677  // some of the callingon route elements may be involved
4678  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4679  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4680  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4681  int RouteStartPosition;
4682  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4683  int PlatformPosition;
4684  // the track vector position of the first stop platfrom
4685  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4686  // not used here
4687  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4688  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4689  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4690  // must be stopped at a signal but not at a location & still in timetable (a)
4692  // no need to check for SignallerStopped as this function only called in Timetable mode
4693  {
4694  Utilities->CallLogPop(711);
4695  return(false);
4696  }
4697  while(true)
4698  {
4699  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4700  // don't look further than 4km ahead (j)
4701  if(Distance > (4000 + LeadElementDistance))
4702  {
4703  Utilities->CallLogPop(967);
4704  return(false);
4705  }
4706  // if find another train on an element in front, before find a valid platform, return false (c)
4707  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4708  {
4709  Utilities->CallLogPop(713);
4710  return(false);
4711  }
4712  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4713  // be facing later on)
4714  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4715  {
4716  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4717  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4718  if(OtherTrain.LeadElement == -1)
4719  {
4720  Utilities->CallLogPop(714);
4721  return(false);
4722  }
4723  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4724  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4725  {
4726  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4727  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4728  (OtherTrain.TrainMode == Signaller))
4729  {
4730  break;
4731  }
4732  else
4733  {
4734  Utilities->CallLogPop(955);
4735  return(false);
4736  }
4737  }
4738  else // (h)
4739  {
4740  break;
4741  }
4742  }
4743  // if reach buffers or exit continuation return false (can set route)
4744  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4745  {
4746  Utilities->CallLogPop(716);
4747  return(false);
4748  }
4749  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4750  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4752  {
4753  Utilities->CallLogPop(717);
4754  return(false);
4755  }
4756  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4757 /*
4758  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4759  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4760  {
4761  Utilities->CallLogPop(718);
4762  return false;
4763  }
4764 */
4765  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4766  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4767  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4768  {
4769  if(StopRequired)
4770  {
4771  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4772  {
4773  if(!PlatformFoundFlag)
4774  {
4775  PlatformPosition = CurrentTrackVectorPosition;
4776  }
4777  // ensure this only set once at first valid platform position - the unrestricted route will end here
4778  PlatformFoundFlag = true;
4779  }
4780  }
4781  }
4782  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4783  // train has to be at station but that has to be before the next forward signal
4784 /*
4785  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4786  {
4787  Utilities->CallLogPop(719);
4788  return false;
4789  }
4790 */
4791  // make sure points are followed correctly (d) & set ExitPos
4792  if(CurrentTrackElement.TrackType == Points)
4793  {
4794  if((EntryPos == 0) || (EntryPos == 2))
4795  {
4796  if(CurrentTrackElement.Attribute == 0)
4797  {
4798  ExitPos = 1;
4799  }
4800  else
4801  {
4802  ExitPos = 3;
4803  }
4804  }
4805  if(EntryPos == 1)
4806  {
4807  if(CurrentTrackElement.Attribute == 0)
4808  {
4809  ExitPos = 0;
4810  }
4811  else
4812  {
4813  Utilities->CallLogPop(720);
4814  return(false);
4815  }
4816  }
4817  if(EntryPos == 3)
4818  {
4819  if(CurrentTrackElement.Attribute == 1)
4820  {
4821  ExitPos = 0;
4822  }
4823  else
4824  {
4825  Utilities->CallLogPop(721);
4826  return(false);
4827  }
4828  }
4829  }
4830  else
4831  {
4832  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4833  }
4834  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4835  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4836  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4837  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4838  if(ElementNumber < 2)
4839  {
4840  SkipRouteCheck = true;
4841  }
4842  else
4843  {
4844  SkipRouteCheck = false;
4845  }
4846  if(ElementNumber == 1) // the stop signal
4847  {
4848  RouteStartPosition = CurrentTrackVectorPosition;
4849  }
4850 /*
4851  if(ElementNumber == 2)
4852  {
4853  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4854  else AutoSigs = false;
4855  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4856  }
4857 */
4858  if(ElementNumber > 1)
4859  {
4860  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4861  {
4862  RouteOrPartRouteSet = true;
4863  }
4864  else
4865  {
4866  RouteOrPartRouteSet = false;
4867  }
4868  }
4869  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4870  {
4871  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4872  {
4873  Utilities->CallLogPop(1859);
4874  return(false);
4875  }
4876  int ExitLink = CurrentTrackElement.Link[ExitPos];
4877  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4878  {
4879  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4880  {
4881  Utilities->CallLogPop(1850);
4882  return(false);
4883  }
4884  }
4885  }
4886  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
4887  if(EntryPos < 2)
4888  {
4889  Distance += CurrentTrackElement.Length01;
4890  }
4891  else
4892  {
4893  Distance += CurrentTrackElement.Length23;
4894  }
4895  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
4896  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
4897  CurrentTrackVectorPosition = NextTrackVectorPosition;
4898  EntryPos = NextEntryPos;
4899  ElementNumber++;
4900  } // while(true)
4901 
4902  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
4903  // from the stop signal (note that it may be last in an autosigs route)
4904  // a single element route at the stop signal should have been removed prior to this function being called (that called before
4905  // this in ClockTimer2)
4906 
4907  // now add elements to the CallonVector
4908  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
4909 
4910  AllRoutes->CallonVector.push_back(CallonEntry);
4911  Utilities->CallLogPop(1860);
4912  return(true); // return false if fail to set route for any reason
4913 }
4914 
4915 // ---------------------------------------------------------------------------
4916 /*
4917  bool TTrain::TimetableFinished()
4918  {
4919  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
4920  {
4921  return true;
4922  }
4923  return false;
4924  }
4925 */
4926 // ---------------------------------------------------------------------------
4927 
4928 AnsiString TTrain::GetTrainHeadCode(int Caller)
4929 
4930 {
4931  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
4932  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
4933 
4934  Utilities->CallLogPop(1452);
4935  return(RepeatHeadCode);
4936 }
4937 
4938 // ---------------------------------------------------------------------------
4939 
4940 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
4941 {
4942  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
4943  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
4944 
4945  Utilities->CallLogPop(1453);
4946  return(RepeatTime);
4947 }
4948 
4949 // ---------------------------------------------------------------------------
4950 
4951 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
4952 {
4953  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
4954  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
4955  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
4956  // first check that train is fully on the railway
4957  bool FrontValid = false, RearValid = false;
4958  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
4959 
4960  if((LeadElement == -1) || (MidElement == -1))
4961  {
4962  TrainToBeJoinedBy = NULL;
4963  Utilities->CallLogPop(2131);
4964  return(false);
4965  }
4967  {
4968  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
4969  FrontValid = true;
4970  }
4972  {
4973  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
4974  RearValid = true;
4975  }
4976  int TrainToBeJoinedByID = -1;
4977 
4978  // first check if on a 2-track element & select correct ID number if so
4979  if(FrontValid)
4980  {
4981  if(FrontAdjacentTrackElement.TrackType == Bridge)
4982  {
4984  {
4985  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
4986  }
4987  else
4988  {
4989  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
4990  }
4991  }
4992  else
4993  {
4994  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
4995  }
4996  }
4997  if((TrainToBeJoinedByID < 0) && RearValid)
4998  {
4999  // first check if on a 2-track element & select correct ID number if so
5000  if(RearAdjacentTrackElement.TrackType == Bridge)
5001  {
5003  {
5004  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5005  }
5006  else
5007  {
5008  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5009  }
5010  }
5011  else
5012  {
5013  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5014  }
5015  }
5016  if(TrainToBeJoinedByID < 0) // no adjacent train
5017  {
5018  TrainToBeJoinedBy = NULL;
5019  Utilities->CallLogPop(2132);
5020  return(false);
5021  }
5022  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5023  if(!TrainToBeJoinedBy->Stopped())
5024  {
5025  TrainToBeJoinedBy = NULL;
5026  Utilities->CallLogPop(2133);
5027  return(false);
5028  }
5029  Utilities->CallLogPop(2134);
5030  return(true);
5031 }
5032 
5033 // ---------------------------------------------------------------------------
5034 
5035 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5036  TDateTime TimetableNonRepeatTime, bool Warning)
5037 /*
5038  Time = timetable time, the time adjustments for repeat trains is carried out internally
5039  Not all messages need this, if not needed a dummy value is required but not used
5040 
5041  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5042  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5043  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5044  //NB for Frh just give terminated message but without event time - don't use this function
5045  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5046  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5047  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5048  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5049  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5050  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5051  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5052  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5053  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5054  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5055  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5056  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5057  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5058  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5059  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5060  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5061  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
5062  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5063  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5064  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5065  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5066  SignallerStop 06:05:40: 2F46 stopped on signaller command
5067  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5068  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5069 */{
5070  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5071  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5072  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5073  int IntMinsLate = 0;
5074 
5075  // need to set it in case MinsLate == 0, since it isn't tested for that
5076  if(ActionType == Arrive)
5077  {
5078  ActionLog = " arrived at ";
5079  }
5080  if(ActionType == Terminate)
5081  {
5082  if(TerminatedMessageSent) // to avoid it being sent twice
5083  {
5084  Utilities->CallLogPop(1104);
5085  return;
5086  }
5087  ActionLog = " terminated at ";
5088  TerminatedMessageSent = true;
5089  }
5090  if(ActionType == Depart)
5091  {
5092  ActionLog = " departed from ";
5093  }
5094  if(ActionType == Pass)
5095  {
5096  ActionLog = " passed ";
5097  }
5098  if(ActionType == Create)
5099  {
5100  ActionLog = " created at ";
5101  }
5102  if(ActionType == Enter)
5103  {
5104  ActionLog = " entered railway at ";
5105  }
5106  if(ActionType == Leave)
5107  {
5108  ActionLog = " left railway at ";
5109  }
5110  if(ActionType == FrontSplit)
5111  {
5112  ActionLog = " split from front to ";
5113  }
5114  if(ActionType == RearSplit)
5115  {
5116  ActionLog = " split from rear to ";
5117  }
5118  if(ActionType == JoinedByOther)
5119  {
5120  ActionLog = " joined by ";
5121  }
5122  if(ActionType == ChangeDirection)
5123  {
5124  ActionLog = " changed direction at ";
5125  }
5126  if(ActionType == NewService)
5127  {
5128  ActionLog = " became new service ";
5129  }
5130  if(ActionType == TakeSignallerControl)
5131  {
5132  ActionLog = " taken under signaller control at ";
5133  }
5134  if(ActionType == RestoreTimetableControl)
5135  {
5136  ActionLog = " restored to timetable control at ";
5137  }
5138  if(ActionType == RemoveTrain)
5139  {
5140  if(Crashed)
5141  {
5142  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5143  }
5144  else if(Derailed)
5145  {
5146  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5147  }
5148  else
5149  {
5150  ActionLog = " REMOVED FROM RAILWAY at ";
5151  }
5152  }
5153  if(ActionType == SignallerMoveForwards)
5154  {
5155  ActionLog = " received signaller authority to proceed";
5156  }
5157  if(ActionType == SignallerStepForward)
5158  {
5159  ActionLog = " received signaller authority to step forward";
5160  }
5161  if(ActionType == SignallerChangeDirection)
5162  {
5163  ActionLog = " changed direction under signaller control at ";
5164  }
5165  if(ActionType == SignallerPassRedSignal)
5166  {
5167  ActionLog = " received signaller authority to pass red signal";
5168  }
5169  if(ActionType == SignallerControlStop)
5170  {
5171  ActionLog = " received signaller instruction to stop";
5172  }
5173  if(ActionType == SignallerStop)
5174  {
5175  ActionLog = " stopped on signaller instruction ";
5176  }
5177  if(ActionType == SignallerJoin)
5178  {
5179  ActionLog = " joined under signaller control by ";
5180  }
5181  if(ActionType == TrainFailure)
5182  {
5183  ActionLog = " suffered an onboard power failure at ";
5184  }
5185  if(ActionType == RepairFailedTrain)
5186  {
5187  ActionLog = " failure repaired at ";
5188  }
5189  if(ActionType == SignallerLeave)
5190  {
5191  ActionLog = " left railway under signaller control at ";
5192  }
5193  if(OtherHeadCode != "")
5194  {
5195  OtherHeadCode += " at ";
5196  }
5197  TDateTime ActualTime = TrainController->TTClockTime;
5198 
5199  if(Warning)
5200  {
5201  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5202  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
5203  }
5204  else
5205  {
5206  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5207  }
5208  bool TimePerformance = true;
5209 
5210  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5211  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5212  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5213  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5214  // SignallerJoin & RepairFailedTrain new at v2.4.0
5215  {
5216  TimePerformance = false;
5217  }
5218  if(TimePerformance)
5219  {
5220  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5221  MinsDelayed = float(MinsLate);
5222  // new v2.2.0 for OpActionPanel, can be positive or negative
5223  if(ActionType == Arrive)
5224  {
5226  }
5227  // since train has just arrived this value is the
5228  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5229  // subtracted from later stop recoverable times.
5230  if(MinsLate < 0)
5231  {
5232  IntMinsLate = int(ceil(MinsLate));
5233  }
5234  if(MinsLate > 0)
5235  {
5236  IntMinsLate = int(floor(MinsLate));
5237  }
5238  if(IntMinsLate == 0)
5239  {
5240  PerfLog = " on time";
5241  }
5242  else if(IntMinsLate == 1)
5243  {
5244  PerfLog = " 1 minute late";
5245  }
5246  else if(IntMinsLate == -1)
5247  {
5248  PerfLog = " 1 minute early";
5249  }
5250  else if(IntMinsLate > 1)
5251  {
5252  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5253  }
5254  else if(IntMinsLate < -1)
5255  {
5256  int PosIntMinsLate = -IntMinsLate;
5257  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5258  }
5259  if(LocationName.Pos('-') > 0)
5260  {
5261  PerfLog = "," + PerfLog;
5262  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5263  }
5264  Display->PerformanceLog(0, BaseLog + PerfLog);
5265  }
5266  else
5267  {
5268  Display->PerformanceLog(1, BaseLog);
5269  }
5270  if(Warning)
5271  {
5272  Display->WarningLog(0, WarningBaseLog);
5273  }
5274  // update statistics
5275  if((ActionType == Arrive) && (IntMinsLate == 0))
5276  {
5278  }
5279  else if((ActionType == Arrive) && (IntMinsLate > 0))
5280  {
5282  TrainController->TotLateArrMins += IntMinsLate;
5283  }
5284  else if((ActionType == Arrive) && (IntMinsLate < 0))
5285  {
5287  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5288  }
5289  else if((ActionType == Pass) && (IntMinsLate == 0))
5290  {
5292  }
5293  else if((ActionType == Pass) && (IntMinsLate > 0))
5294  {
5296  TrainController->TotLatePassMins += IntMinsLate;
5297  }
5298  else if((ActionType == Pass) && (IntMinsLate < 0))
5299  {
5301  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5302  }
5303  else if((ActionType == Depart) && (IntMinsLate == 0))
5304  {
5306  }
5307  else if((ActionType == Depart) && (IntMinsLate > 0))
5308  {
5310  TrainController->TotLateDepMins += IntMinsLate;
5311  }
5312  Utilities->CallLogPop(968);
5313 }
5314 
5315 // ---------------------------------------------------------------------------
5316 
5317 void TTrain::TrainHasFailed(int Caller)
5318 {
5319  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5320  if(Crashed || Derailed || DerailPending)
5321  {
5322  TrainFailurePending = false;
5323  Utilities->CallLogPop(2135);
5324  return;
5325  }
5326  AnsiString LocName = "";
5327 
5328  if(LeadElement > -1)
5329  {
5331  }
5332  if((LocName == "") && (MidElement > -1))
5333  {
5335  }
5336  if((LocName == "") && LeadElement > -1)
5337  {
5338  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5339  }
5340  if((LocName == "") && (MidElement > -1))
5341  {
5342  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5343  }
5344  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5345  TrainFailed = true;
5346  TrainFailurePending = false;
5348  PowerAtRail = 0.08;
5349  AValue = sqrt(2 * PowerAtRail / Mass);
5351  // TrainFailed only called when PlotElements properly set to lead, Mid & Lag elements
5352  if(Stopped())
5353  {
5354  EntrySpeed = 0;
5355  ExitSpeedHalf = 0;
5356  ExitSpeedFull = 0;
5357  MaxExitSpeed = 0;
5358  BrakeRate = 0;
5359  StoppedWithoutPower = true;
5360  }
5362  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5363  // true for warning, TDateTime not used
5364  Utilities->CallLogPop(2136);
5365 }
5366 
5367 // ---------------------------------------------------------------------------
5368 
5369 void TTrain::FrontTrainSplit(int Caller)
5370 {
5371 /*
5372  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5373  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5374  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5375  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5376 */
5377  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5378  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5379  if(PowerAtRail < 1)
5380  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5381  {
5383  {
5384  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5385  }
5387  Utilities->CallLogPop(2137);
5388  return;
5389  }
5390  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5391 
5392  if(LocationName == "")
5393  {
5394  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5395  }
5396  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5397  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5398  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5400 
5401  // determine all positions & exits
5402  if(LocationName != "")
5403  {
5404  // if message given only call at ~5 sec intervals
5406  {
5407  FirstNamedElementPos = LeadElement;
5408  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5409  // check if possible with LeadElement as First
5410  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5411  {
5412  FirstNamedElementPos = MidElement;
5413  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5414  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5415  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5416  {
5418  {
5419  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5421  }
5422  Utilities->CallLogPop(1009);
5423  return;
5424  }
5425  }
5426  else
5427  {
5428  // if first is possible then check if all 4 positions at location, and if not try the second
5429  int LeadPosA = FirstNamedElementPos;
5430  int LeadPosB = FirstNamedLinkedElementPos;
5431  int LeadPosC = SecondNamedElementPos;
5432  int LeadPosD = SecondNamedLinkedElementPos;
5433  // count number of positions that are at the location
5434  int LeadNumAtLoc = 0;
5435  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5436  {
5437  LeadNumAtLoc++;
5438  }
5439  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5440  {
5441  LeadNumAtLoc++;
5442  }
5443  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5444  {
5445  LeadNumAtLoc++;
5446  }
5447  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5448  {
5449  LeadNumAtLoc++;
5450  }
5451  if(LeadNumAtLoc < 4)
5452  {
5453  FirstNamedElementPos = MidElement;
5454  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5455  SecondNamedLinkedElementPos)) // restore originals
5456  {
5457  FirstNamedElementPos = LeadPosA;
5458  FirstNamedLinkedElementPos = LeadPosB;
5459  SecondNamedElementPos = LeadPosC;
5460  SecondNamedLinkedElementPos = LeadPosD;
5461  }
5462  else // count number at location
5463  {
5464  int MidNumAtLoc = 0;
5465  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5466  {
5467  MidNumAtLoc++;
5468  }
5469  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5470  {
5471  MidNumAtLoc++;
5472  }
5473  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5474  {
5475  MidNumAtLoc++;
5476  }
5477  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5478  {
5479  MidNumAtLoc++;
5480  }
5481  if(LeadNumAtLoc > MidNumAtLoc)
5482  // change back, else keep new values
5483  {
5484  FirstNamedElementPos = LeadPosA;
5485  FirstNamedLinkedElementPos = LeadPosB;
5486  SecondNamedElementPos = LeadPosC;
5487  SecondNamedLinkedElementPos = LeadPosD;
5488  }
5489  }
5490  }
5491  }
5492  }
5493  else
5494  {
5495  Utilities->CallLogPop(1791);
5496  return;
5497  }
5498  }
5499  else
5500  {
5501  throw Exception("Error - LocationName not set in FrontTrainSplit");
5502  }
5503  // set front & rear train parameters
5504  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5505  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5506  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5507  if(LeadElement == FirstNamedElementPos)
5508  {
5509  if(MidElement == SecondNamedElementPos)
5510  {
5511  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5512  FrontTrainRearPosition = FirstNamedElementPos;
5513  RearTrainFrontPosition = SecondNamedElementPos;
5514  RearTrainRearPosition = SecondNamedLinkedElementPos;
5515  }
5516  else // MidElement must == FirstNamedLinkedElementPos
5517  {
5518  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5519  FrontTrainRearPosition = SecondNamedElementPos;
5520  RearTrainFrontPosition = FirstNamedElementPos;
5521  RearTrainRearPosition = FirstNamedLinkedElementPos;
5522  }
5523  }
5524  else // MidElement == FirstNamedElementPos
5525  {
5526  if(LeadElement == SecondNamedElementPos)
5527  {
5528  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5529  FrontTrainRearPosition = SecondNamedElementPos;
5530  RearTrainFrontPosition = FirstNamedElementPos;
5531  RearTrainRearPosition = FirstNamedLinkedElementPos;
5532  }
5533  else // LeadElement must == FirstNamedLinkedElementPos
5534  {
5535  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5536  FrontTrainRearPosition = FirstNamedElementPos;
5537  RearTrainFrontPosition = SecondNamedElementPos;
5538  RearTrainRearPosition = SecondNamedLinkedElementPos;
5539  }
5540  }
5541  RearTrainExitPos = -1;
5542  for(int x = 0; x < 4; x++)
5543  {
5544  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5545  {
5546  RearTrainExitPos = x;
5547  break;
5548  }
5549  }
5550  if(RearTrainExitPos == -1)
5551  {
5552  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5553  }
5554  FrontTrainExitPos = -1;
5555  for(int x = 0; x < 4; x++)
5556  {
5557  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5558  {
5559  FrontTrainExitPos = x;
5560  break;
5561  }
5562  }
5563  if(FrontTrainExitPos == -1)
5564  {
5565  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5566  }
5567  // check no train (apart from self) on any of the 4 elements & fail if so
5568  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5569  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5570 
5571  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5572  {
5573  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5574  }
5575  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5576  {
5577  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5578  }
5579  else
5580  {
5581  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5582  }
5583  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5584  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5585  // can't be a bridge
5586  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5587  // can't be a bridge
5588  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5589  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5590 
5591  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5592  {
5593  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5594  }
5595  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5596  {
5597  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5598  }
5599  else
5600  {
5601  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5602  }
5603  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5604  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5605  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5606  {
5608  {
5611  }
5612  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5613  Utilities->CallLogPop(1010);
5614  return;
5615  }
5617  {
5619  }
5620  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5621  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5622  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5623  // variable as it is needed for setting up the new train
5624  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5625 
5626  UnplotTrain(0);
5627  StartSpeed = 0;
5628  RearStartElement = RearTrainRearPosition;
5629  RearStartExitPos = RearTrainExitPos;
5630  StoppedAtLocation = true;
5631  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5632  {
5633  StoppedWithoutPower = true;
5634  }
5635  PlotStartPosition(3);
5640 
5641  Mass = Mass / 2;
5642  MaxBrakeRate = MaxBrakeRate / 2;
5643  PowerAtRail = PowerAtRail / 2;
5644  AValue = sqrt(2 * PowerAtRail / Mass);
5645  // shouldn't change but include in case not set earlier
5646 
5647  // create new front train
5648 /*
5649  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5650  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5651  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5652 */
5653  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5654  TActionEventType EventType = NoEvent;
5655 
5656  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5657  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5658  // false for SignallerControl
5659  {
5660  Utilities->CallLogPop(1721); // EventType not used here
5661  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5662  // another train, in which case a message will have been sent to the perf log, also might well clear later
5663  // when other train moves away
5664  return;
5665  }
5666  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5667  // see mods in UpdateTrain for v1.3.2
5668  TrainController->TrainAdded = true;
5669 
5670  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5671 
5672  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5673  TTOD.RunningEntry = Running;
5674  Utilities->CallLogPop(998);
5675 }
5676 
5677 // ---------------------------------------------------------------------------
5678 
5679 void TTrain::RearTrainSplit(int Caller)
5680 {
5681 /*
5682  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5683  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5684  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5685  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5686 */
5687  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5688  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5689  if(PowerAtRail < 1)
5690  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5691  {
5693  {
5694  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5695  }
5697  Utilities->CallLogPop(2138);
5698  return;
5699  }
5700  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5701 
5702  if(LocationName == "")
5703  {
5704  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5705  }
5706  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5707  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5708  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5710 
5711  // determine all positions & exits
5712  if(LocationName != "")
5713  {
5714  // if message given only call at ~5 sec intervals
5716  {
5717  FirstNamedElementPos = LeadElement;
5718  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5719  SecondNamedLinkedElementPos))
5720  {
5721  FirstNamedElementPos = MidElement;
5722  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5723  SecondNamedLinkedElementPos))
5724  {
5726  {
5727  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5729  }
5730  Utilities->CallLogPop(1013);
5731  return;
5732  }
5733  }
5734  else
5735  {
5736  // if first is possible then check if all 4 positions at location, and if not try the second
5737  int LeadPosA = FirstNamedElementPos;
5738  int LeadPosB = FirstNamedLinkedElementPos;
5739  int LeadPosC = SecondNamedElementPos;
5740  int LeadPosD = SecondNamedLinkedElementPos;
5741  // count number of positions that are at the location
5742  int LeadNumAtLoc = 0;
5743  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5744  {
5745  LeadNumAtLoc++;
5746  }
5747  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5748  {
5749  LeadNumAtLoc++;
5750  }
5751  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5752  {
5753  LeadNumAtLoc++;
5754  }
5755  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5756  {
5757  LeadNumAtLoc++;
5758  }
5759  if(LeadNumAtLoc < 4)
5760  {
5761  FirstNamedElementPos = MidElement;
5762  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5763  SecondNamedLinkedElementPos)) // restore originals
5764  {
5765  FirstNamedElementPos = LeadPosA;
5766  FirstNamedLinkedElementPos = LeadPosB;
5767  SecondNamedElementPos = LeadPosC;
5768  SecondNamedLinkedElementPos = LeadPosD;
5769  }
5770  else // count number at location
5771  {
5772  int MidNumAtLoc = 0;
5773  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5774  {
5775  MidNumAtLoc++;
5776  }
5777  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5778  {
5779  MidNumAtLoc++;
5780  }
5781  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5782  {
5783  MidNumAtLoc++;
5784  }
5785  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5786  {
5787  MidNumAtLoc++;
5788  }
5789  if(LeadNumAtLoc > MidNumAtLoc)
5790  // change back, else keep new values
5791  {
5792  FirstNamedElementPos = LeadPosA;
5793  FirstNamedLinkedElementPos = LeadPosB;
5794  SecondNamedElementPos = LeadPosC;
5795  SecondNamedLinkedElementPos = LeadPosD;
5796  }
5797  }
5798  }
5799  }
5800  }
5801  else
5802  {
5803  Utilities->CallLogPop(1792);
5804  return;
5805  }
5806  }
5807  else
5808  {
5809  throw Exception("Error - LocationName not set in RearTrainSplit");
5810  }
5811  // set front & rear train parameters
5812  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5813  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5814  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5815  if(LeadElement == FirstNamedElementPos)
5816  {
5817  if(MidElement == SecondNamedElementPos)
5818  {
5819  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5820  FrontTrainRearPosition = FirstNamedElementPos;
5821  RearTrainFrontPosition = SecondNamedElementPos;
5822  RearTrainRearPosition = SecondNamedLinkedElementPos;
5823  }
5824  else // MidElement must == FirstNamedLinkedElementPos
5825  {
5826  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5827  FrontTrainRearPosition = SecondNamedElementPos;
5828  RearTrainFrontPosition = FirstNamedElementPos;
5829  RearTrainRearPosition = FirstNamedLinkedElementPos;
5830  }
5831  }
5832  else // MidElement == FirstNamedElementPos
5833  {
5834  if(LeadElement == SecondNamedElementPos)
5835  {
5836  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5837  FrontTrainRearPosition = SecondNamedElementPos;
5838  RearTrainFrontPosition = FirstNamedElementPos;
5839  RearTrainRearPosition = FirstNamedLinkedElementPos;
5840  }
5841  else // LeadElement must == FirstNamedLinkedElementPos
5842  {
5843  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5844  FrontTrainRearPosition = FirstNamedElementPos;
5845  RearTrainFrontPosition = SecondNamedElementPos;
5846  RearTrainRearPosition = SecondNamedLinkedElementPos;
5847  }
5848  }
5849  RearTrainExitPos = -1;
5850  for(int x = 0; x < 4; x++)
5851  {
5852  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5853  {
5854  RearTrainExitPos = x;
5855  break;
5856  }
5857  }
5858  if(RearTrainExitPos == -1)
5859  {
5860  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5861  }
5862  FrontTrainExitPos = -1;
5863  for(int x = 0; x < 4; x++)
5864  {
5865  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5866  {
5867  FrontTrainExitPos = x;
5868  break;
5869  }
5870  }
5871  if(FrontTrainExitPos == -1)
5872  {
5873  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
5874  }
5875  // check no train (apart from self) on any of the 4 elements & fail if so
5876  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5877  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
5878 
5879  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5880  {
5881  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5882  }
5883  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5884  {
5885  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5886  }
5887  else
5888  {
5889  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5890  }
5891  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5892  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
5893  // can't be a bridge
5894  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
5895  // can't be a bridge
5896  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5897  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
5898 
5899  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5900  {
5901  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5902  }
5903  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5904  {
5905  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5906  }
5907  else
5908  {
5909  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5910  }
5911  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5912  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5913  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5914  {
5916  {
5919  }
5920  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5921  Utilities->CallLogPop(1014);
5922  return;
5923  }
5925  {
5927  }
5928  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
5929  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5930  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5931  // variable as it is needed for setting up the new train
5932  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5933 
5934  UnplotTrain(1);
5935  StartSpeed = 0;
5936  RearStartElement = FrontTrainRearPosition;
5937  RearStartExitPos = FrontTrainExitPos;
5938  StoppedAtLocation = true;
5939  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5940  {
5941  StoppedWithoutPower = true;
5942  }
5943  PlotStartPosition(4);
5948  Mass = Mass / 2;
5949  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
5950  MaxBrakeRate = MaxBrakeRate / 2;
5951  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
5952  PowerAtRail = PowerAtRail / 2;
5953  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
5954  AValue = sqrt(2 * PowerAtRail / Mass);
5955  // shouldn't change but include in case not set earlier
5956 
5957  // create new rear train
5958 /*
5959  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5960  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5961  int RepeatNumber, int IncrementalMinutes)
5962 */
5963  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5964  TActionEventType EventType = NoEvent;
5965 
5966  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5967  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5968  // false for SignallerControl
5969  {
5970  Utilities->CallLogPop(1722); // EventType not used here
5971  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5972  // another train, in which case a message will have been sent to the perf log, also might well clear later
5973  // when other train moves away
5974  return;
5975  }
5976  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5977  // see mods in UpdateTrain for v1.3.2
5978  TrainController->TrainAdded = true;
5979  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5980 
5981  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5982  TTOD.RunningEntry = Running;
5983  Utilities->CallLogPop(1015);
5984 }
5985 
5986 // ---------------------------------------------------------------------------
5987 
5988 void TTrain::FinishJoin(int Caller)
5989 {
5990  if(FinishJoinLogSent == false)
5991  {
5992  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
5993  FinishJoinLogSent = true; // so don't keep logging this event
5994  // don't need to reset it to false after the event as the train is deleted
5995  }
5996  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
5997  if(TrainFailed)
5998  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
5999  {
6001  {
6002  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6003  }
6005  Utilities->CallLogPop(2139);
6006  return;
6007  }
6008  if(TrainGone)
6009  // this means that the train has already joined the other & is awaiting deletion by TrainController
6010  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6011  // on from jbo & TrainToJoinIsAdjacent returns false
6012  {
6013  Utilities->CallLogPop(1035);
6014  return;
6015  }
6016  TTrain *TrainToJoin;
6018 
6019  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6020  {
6022  {
6023  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6026  }
6027  Utilities->CallLogPop(1030);
6028  return; // keep this here in case need to add code before final return
6029  }
6030  // no need to clear error report flag here, cleared in jbo function
6031  // No need to set TimetableFinished, done in jbo function
6032  Utilities->CallLogPop(1031);
6033 }
6034 
6035 // ---------------------------------------------------------------------------
6036 
6037 void TTrain::JoinedBy(int Caller)
6038 {
6039  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6040  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6041  if(PowerAtRail < 1)
6042  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6043  {
6045  {
6046  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6047  }
6049  Utilities->CallLogPop(2140);
6050  return;
6051  }
6052  TTrain *TrainToBeJoinedBy;
6054 
6055  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6056  {
6058  {
6059  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6062  }
6063  LastActionDelayFlag = true;
6064  // need to update LastActionTime if this train first to arrive as need 30s after
6065  // both adjacent before the join
6066  Utilities->CallLogPop(1032);
6067  return;
6068  }
6069  // here when other train is adjacent
6071  {
6073  // need to update this as need 30s after both adjacent before the join
6074  LastActionDelayFlag = false;
6075  Utilities->CallLogPop(1033);
6076  return;
6077  }
6078  // here when other train is adjacent & 30 secs elapsed since both adjacent
6079 
6080  // set new values for mass etc
6081  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6082  {
6083  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6084  }
6085  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6086  double OwnBrakeForce = MaxBrakeRate * Mass;
6087  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6088 
6089  Mass += TrainToBeJoinedBy->Mass;
6090  MaxBrakeRate = CombinedBrakeRate;
6091  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6092  AValue = sqrt(2 * PowerAtRail / Mass);
6093 
6095  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6096  TrainToBeJoinedBy->TimetableFinished = true;
6097  TrainToBeJoinedBy->TrainGone = true;
6098  // this will cause other train to be deleted
6099  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6103  Utilities->CallLogPop(1034);
6104 }
6105 
6106 // ---------------------------------------------------------------------------
6107 
6109 {
6110  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6111  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6112  if(PowerAtRail < 1)
6113  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6114  {
6116  {
6117  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6118  }
6119  ZeroPowerNoCDTMessage = true;
6120  Utilities->CallLogPop(2141);
6121  return;
6122  }
6123  TColor TempColour = BackgroundColour;
6124 
6125  UnplotTrain(2);
6128  StartSpeed = 0;
6129  StoppedAtLocation = true;
6130  PlotStartPosition(1);
6131  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6132  // plot same as was - should always be pale green
6136 
6137  //now erase a stub route if there is one, added at v2.5.1
6138  //first element of route is now immediately behind the train (i.e. next to MidElement)
6139  if(MidEntryPos >= 0)
6140  {
6141  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6142  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6143  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6144  int RouteNumber = -1;
6145  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6146  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6147  {
6148  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6149  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6150  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6151  {
6152  bool FirstPass = true; //added at v2.8.0
6153  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6154  {
6155  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6156  int TVPos2 = PDE.GetTrackVectorPosition();
6157  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6158  {
6159  break;
6160  }
6161  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6163  {
6164  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6165  }
6166  else
6167  {
6168  break;
6169  }
6170  FirstPass = false;
6171  }
6172  AllRoutes->RebuildRailwayFlag = true;
6173  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6174  }
6175  }
6176  }
6177  Utilities->CallLogPop(1012);
6178 }
6179 
6180 // ---------------------------------------------------------------------------
6181 
6182 void TTrain::NewTrainService(int Caller)
6183 // change to new train, give new service message
6184 {
6185  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6186  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6187  if(PowerAtRail < 1)
6188  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6189  {
6191  {
6192  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6193  }
6195  Utilities->CallLogPop(2142);
6196  return;
6197  }
6199 
6201  UnplotTrain(3);
6204  StartSpeed = 0;
6209  HeadCode = NewHeadCode;
6210  StoppedAtLocation = true;
6211  PlotStartPosition(5);
6213  // pale green
6216  TerminatedMessageSent = false;
6217  Utilities->CallLogPop(1022);
6218 }
6219 
6220 // ---------------------------------------------------------------------------
6221 
6222 void TTrain::RemainHere(int Caller)
6223 {
6224  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6225  if(RemainHereLogNotSent) // to prevent repeated logs
6226  {
6227  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6228  RemainHereLogNotSent = false;
6229  }
6231  {
6232  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
6235  TerminatedMessageSent = true;
6236  }
6237  TimetableFinished = true;
6238  Utilities->CallLogPop(1023);
6239 }
6240 
6241 // ---------------------------------------------------------------------------
6242 
6243 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6244 /*
6245  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6246  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6247  except where an action is a departure, starting at the current value for the pointer
6248  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6249  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6250 */{
6251  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6252  {
6253  return; // if remove train that starts under signaller control no messages needed
6254 
6255  }
6256  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6257  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6258  if(IncNum > 0)
6259  {
6260  for(int x = 0; x < IncNum; x++)
6261  {
6262  if(x > 0)
6263  {
6264  Ptr++;
6265  }
6266  // arrival - no need to test for termination as this section only covers missed actions up to the
6267  // arrival point - may terminate later but that not missed
6268  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6269  {
6271  }
6272  // arrival & departure
6273  if(Ptr->FormatType == TimeTimeLoc)
6274  {
6276  }
6277  // departure
6278  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6279  {
6280  continue; // skip TimeLoc departures, message given for arrivals
6281  }
6282  // pass
6283  else if(Ptr->FormatType == PassTime)
6284  {
6286  }
6287  // split
6288  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6289  {
6291  }
6292  // jbo
6293  else if(Ptr->Command == "jbo")
6294  {
6296  }
6297  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6298  // be starts, finishes or cdt
6299  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6300  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6301  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6302  (Ptr->FormatType == Repeat))
6303  {
6304  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6305  }
6306  }
6307  }
6308  else
6309  {
6310  bool IncludeFER = false;
6311  if(IncNum == -1)
6312  {
6313  IncludeFER = true;
6314  }
6315  while(true) // finish commands & repeats break out of loop
6316  {
6317  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6318  if(!IncludeFER && (Ptr->Command == "Fer"))
6319  {
6320  break;
6321  }
6322  // Fer & included
6323  else if(IncludeFER && (Ptr->Command == "Fer"))
6324  {
6326  break;
6327  }
6328  // Repeat
6329  else if(Ptr->FormatType == Repeat)
6330  {
6331  break;
6332  }
6333  // Fjo
6334  else if(Ptr->Command == "Fjo")
6335  {
6337  break;
6338  }
6339  // Frh
6340  else if(Ptr->Command == "Frh")
6341  {
6343  {
6345  TerminatedMessageSent = true;
6346  }
6347  break;
6348  }
6349  // Frh-sh
6350  else if(Ptr->Command == "Frh-sh")
6351  {
6353  {
6355  TerminatedMessageSent = true;
6356  }
6357  break;
6358  }
6359  // Fns, F-nshs, Fns-sh
6360  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6361  {
6363  break;
6364  }
6365  // end of breakout actions
6366  // arrival
6367  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6368  {
6369  if(IsTrainTerminating(1))
6370  {
6372  TerminatedMessageSent = true;
6373  }
6374  else
6375  {
6377  }
6378  }
6379  // arrival & departure
6380  else if(Ptr->FormatType == TimeTimeLoc)
6381  {
6383  }
6384  // departure
6385  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6386  {
6387  Ptr++;
6388  continue; // skip TimeLoc departures, message given for arrivals
6389  }
6390  // pass
6391  else if(Ptr->FormatType == PassTime)
6392  {
6394  }
6395  // split
6396  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6397  {
6399  }
6400  // jbo
6401  else if(Ptr->Command == "jbo")
6402  {
6404  }
6405  // cdt
6406  else if(Ptr->Command == "cdt")
6407  {
6409  }
6410  // Errors
6411  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6412  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6413  {
6414  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6415  }
6416  Ptr++;
6417  }
6418  TimetableFinished = true;
6419  }
6420  Utilities->CallLogPop(1021);
6421 }
6422 
6423 // ---------------------------------------------------------------------------
6424 
6425 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6426 // ensure same repeatnumber
6427 {
6428  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6430 
6431  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6432  {
6433  Utilities->CallLogPop(1024);
6434  return(false);
6435  }
6436  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6437  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6438  {
6439  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6440  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6441  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6442  {
6443  Utilities->CallLogPop(1025);
6444  return(true);
6445  }
6446  }
6447  Utilities->CallLogPop(1026);
6448  return(false);
6449 }
6450 
6451 // ---------------------------------------------------------------------------
6452 
6453 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6454 // ensure same repeatnumber
6455 {
6456  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6457  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6458 
6459  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6460  {
6461  Utilities->CallLogPop(1027);
6462  return(false);
6463  }
6464  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6465  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6466  {
6467  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6468  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6469  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6470  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6471  {
6472  Utilities->CallLogPop(1028);
6473  return(true);
6474  }
6475  }
6476  Utilities->CallLogPop(1029);
6477  return(false);
6478 }
6479 
6480 // ---------------------------------------------------------------------------
6481 
6483 {
6484  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6485  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6486  if(PowerAtRail < 1)
6487  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6488  {
6490  {
6491  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6492  }
6494  Utilities->CallLogPop(2143);
6495  return;
6496  }
6497  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6498 
6500  UnplotTrain(4);
6503  StartSpeed = 0;
6508  HeadCode = NewHeadCode;
6509  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6510  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6511  StoppedAtLocation = true;
6512  PlotStartPosition(6);
6514  // pale green
6517  TerminatedMessageSent = false;
6518  Utilities->CallLogPop(1078);
6519 }
6520 
6521 // ---------------------------------------------------------------------------
6522 
6524 // need to check whether all repeats finished or not
6525 {
6526  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6527  if(RemainHereLogNotSent) // to prevent repeated logs
6528  {
6529  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6530  RemainHereLogNotSent = false;
6531  }
6533  // finished all repeats
6534  {
6536  {
6539  TerminatedMessageSent = true;
6540  // no need to clear message as no more actions
6541  }
6542  TimetableFinished = true;
6543  Utilities->CallLogPop(1080);
6544  return;
6545  }
6546  if(PowerAtRail < 1)
6547  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6548  {
6550  {
6551  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6552  }
6554  Utilities->CallLogPop(2144);
6555  return;
6556  }
6557  int TempRepeatNumber = RepeatNumber + 1;
6558  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6559  // until after LogAction or the wrong time will be used
6560  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6561 
6563  RepeatNumber++;
6564  UnplotTrain(5);
6567  StartSpeed = 0;
6572  HeadCode = NewHeadCode;
6573  StoppedAtLocation = true;
6574  PlotStartPosition(7);
6576  // pale green
6579  TerminatedMessageSent = false;
6580  Utilities->CallLogPop(1079);
6581 }
6582 
6583 // ---------------------------------------------------------------------------
6584 
6586 {
6587  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6588  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6589  if(PowerAtRail < 1)
6590  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6591  {
6593  {
6594  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6595  }
6597  Utilities->CallLogPop(2145);
6598  return;
6599  }
6601  // finished all repeats
6602  {
6603  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6605  RepeatNumber = 0;
6606  UnplotTrain(6);
6609  StartSpeed = 0;
6611  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6612  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6614  HeadCode = NewHeadCode;
6615  StoppedAtLocation = true;
6616  PlotStartPosition(9);
6620  TerminatedMessageSent = false;
6621  Utilities->CallLogPop(1081);
6622  return;
6623  }
6624  int TempRepeatNumber = RepeatNumber + 1;
6625  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6626  // until after LogAction or the wrong time will be used
6627  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6628 
6630  RepeatNumber++;
6631  UnplotTrain(7);
6634  StartSpeed = 0;
6639  HeadCode = NewHeadCode;
6640  StoppedAtLocation = true;
6641  PlotStartPosition(8);
6643  // pale green
6646  TerminatedMessageSent = false;
6647  Utilities->CallLogPop(1082);
6648 }
6649 
6650 // ---------------------------------------------------------------------------
6651 
6653 {
6654  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6655  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6656  // must be preceded by a TimeLoc departure
6657  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6658  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6659  {
6661  {
6662  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6663  {
6664  Utilities->CallLogPop(1083);
6665  return(false);
6666  }
6667  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6668  {
6669  Utilities->CallLogPop(1084);
6670  return(true);
6671  }
6672  }
6673  }
6674  Utilities->CallLogPop(1085);
6675  return(false);
6676 }
6677 
6678 // ---------------------------------------------------------------------------
6679 
6680 bool TTrain::AbleToMove(int Caller)
6681 {
6682  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6683  bool Able = true;
6684 
6685  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0 &
6686  {
6687  // StoppedForTrainInFront removed as tested below
6688  Able = false;
6689  Utilities->CallLogPop(2146); // added v2.4.0
6690  return(Able); // added v2.4.0
6691  }
6692  if(LeadElement > -1)
6693  {
6694  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6695  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6696  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront)
6697  {
6698  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6699  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6700  {
6701  Able = true;
6702  StoppedForTrainInFront = false;
6703  }
6704  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6705  {
6706  Able = true;
6707  StoppedForTrainInFront = false;
6708  }
6709  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6710  {
6711  Able = true;
6712  StoppedForTrainInFront = false;
6713  }
6714  }
6715  else
6716  {
6718  {
6719  Able = false;
6720  }
6721  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6722  }
6723  }
6724  else // leaving at a continuation so keep going
6725  {
6726  Able = true;
6727  StoppedForTrainInFront = false;
6728  }
6729  Utilities->CallLogPop(1454);
6730  return(Able);
6731 }
6732 
6733 // ---------------------------------------------------------------------------
6734 
6736 {
6737  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6738  // won't be set; if there is a train then set StoppedForTrainInFront
6739  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6740  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6741  if(LeadElement == -1) // exiting at continuation
6742  {
6743  Utilities->CallLogPop(2045);
6744  return(false);
6745  }
6746  // end of addition
6747  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6748  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6749 
6750  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6751  {
6752  StoppedForTrainInFront = true;
6753  Utilities->CallLogPop(1455);
6754  return(false);
6755  }
6756  else
6757  {
6758  Utilities->CallLogPop(1456);
6760  // StoppedWithoutPower added v2.4.0
6761  }
6762 }
6763 
6764 // ---------------------------------------------------------------------------
6765 
6767 {
6768  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6769  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6770  TColor TempColour = BackgroundColour;
6771 
6772  UnplotTrain(8);
6775  StartSpeed = 0;
6776  PlotStartPosition(2);
6777  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6778 
6779  //now erase a stub route if there is one, added at v2.5.1
6780  //first element of route is now immediately behind the train (i.e. next to MidElement)
6781  if(MidEntryPos >= 0)
6782  {
6783  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6784  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6785  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6786  int RouteNumber = -1;
6787  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6788  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6789  {
6790  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6791  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6792  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6793  {
6794  bool FirstPass = true; //added at v2.8.0
6795  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a route extends to another signal
6796  {
6797  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6798  int TVPos2 = PDE.GetTrackVectorPosition();
6799  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6800  {
6801  break;
6802  }
6803  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6805  {
6806  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6807  }
6808  else
6809  {
6810  break;
6811  }
6812  FirstPass = false;
6813  }
6814  AllRoutes->RebuildRailwayFlag = true;
6815  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6816  }
6817  }
6818  }
6819  Utilities->CallLogPop(1102);
6820 }
6821 
6822 // ---------------------------------------------------------------------------
6823 
6825 {
6826  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6827  ",FloatingLabelNextString" + "," + HeadCode);
6828  AnsiString RetStr = "", LocationName = "";
6829 
6830  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6831  {
6832  throw Exception("Error - start entry in FloatingLabelNextString");
6833  }
6834  if(Ptr->FormatType == TimeTimeLoc)
6835  {
6836  if(TrainMode == Timetable)
6837  {
6838  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6839  // not arrived yet in tt mode
6840  {
6841  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
6842  }
6843  else
6844  {
6845  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
6846  }
6847  }
6848  else // TrainMode == Signaller
6849  {
6850  if(!DepartureTimeSet) // not arrived yet
6851  {
6852  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
6853  }
6854  else
6855  {
6856  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
6857  }
6858  }
6859  }
6860  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6861  {
6862  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
6863  }
6864  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6865  {
6866  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
6867  }
6868  else if(Ptr->FormatType == PassTime) // new
6869  {
6870  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
6871  }
6872  else if(Ptr->Command == "Fns")
6873  {
6874  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
6875  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
6876  RetStr = CheckNewServiceDepartureTime(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6877  }
6878  else if(Ptr->Command == "F-nshs")
6879  {
6880  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
6882  RetStr = CheckNewServiceDepartureTime(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6883  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6884  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6885  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6886  }
6887  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6888  {
6889  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6890  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
6891  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6892  RetStr = CheckNewServiceDepartureTime(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6893  }
6894  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6895  {
6896  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6897  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
6898  RetStr = CheckNewServiceDepartureTime(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6899  }
6900  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6901  {
6902  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6903  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
6904  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6905  RetStr = CheckNewServiceDepartureTime(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6906  }
6907  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6908  {
6909  RetStr ="None, train terminated at " + Ptr->LocationName;
6910  }
6911  else if(Ptr->Command == "Frh")
6912  {
6913  RetStr = "None, train terminated at " + Ptr->LocationName;
6914  }
6915  else if(Ptr->Command == "Fer")
6916  {
6917  AnsiString AllowedExits = "";
6918  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime)) + AllowedExits;
6919  }
6920  else if(Ptr->Command == "Fjo")
6921  {
6922  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
6924  }
6925  else if(Ptr->Command == "jbo")
6926  {
6927  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6928  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
6929  }
6930  else if(Ptr->Command == "fsp")
6931  {
6932  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6933  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
6934  }
6935  else if(Ptr->Command == "rsp")
6936  {
6937  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6938  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
6939  }
6940  else if(Ptr->Command == "cdt")
6941  {
6942  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
6943  }
6944  Utilities->CallLogPop(1124);
6945  return(RetStr);
6946 }
6947 
6948 // ---------------------------------------------------------------------------
6949 
6950 AnsiString TTrain::CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
6951 {
6952  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
6953  + AnsiString(RptNum) + ",CheckNewServiceDepartureTime," + HeadCode);
6954  AnsiString DepTime = "", EventTime = "";
6955  bool CDTFlag = false; //reports if train changes direction before departs
6956  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
6957  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
6958  {
6959  if(AVI->Command == "cdt")
6960  {
6961  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
6962  continue;
6963  }
6964  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
6965  {
6966  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
6967  RetStr += "\nNew service splits at " + EventTime;
6968  Utilities->CallLogPop(2234);
6969  return(RetStr);
6970  }
6971  if(AVI->Command == "jbo")
6972  {
6973  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
6974  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
6975  Utilities->CallLogPop(2235);
6976  return(RetStr);
6977  }
6978  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
6979  {
6980  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
6981  if(CDTFlag)
6982  {
6983  RetStr += "\nNew service changes direction then departs at " + DepTime;
6984  }
6985  else
6986  {
6987  RetStr += "\nNew service departs at " + DepTime;
6988  }
6989  Utilities->CallLogPop(2236);
6990  return(RetStr);
6991  }
6992  }
6993  Utilities->CallLogPop(2208);
6994  return(RetStr); //if reach here then RetStr doesn't change
6995 }
6996 
6997 // ---------------------------------------------------------------------------
6998 
7000 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7001 {
7002  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7003  ",FloatingTimetableString" + "," + HeadCode);
7004  AnsiString RetStr = "", PartStr = "";
7005  int Count = 0;
7006 
7007  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7008  // can start in signaller control so exclude this
7009  {
7010  throw Exception("Error - start entry in FloatingTimetableString");
7011  }
7012  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTime Loc check later
7013  bool FirstPass = true;
7014  Ptr--; // because incremented at start of loop
7015 
7016  // different first TimeTimeLoc display if in signaller control
7017  do
7018  {
7019  Ptr++;
7020  if((Ptr->FormatType == Repeat) || TimetableFinished)
7021  {
7022  break;
7023  }
7024  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7025  {
7026  AnsiString TrainLoc = "";
7027  if(TrainMode == Timetable)
7028  {
7029  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7030  {
7031  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7032  }
7033  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7034  {
7035  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7036  }
7037  else
7038  {
7039  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7040  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7041  Count++; // because there are 2 entries
7042  }
7043  }
7044  else // TrainMode == Signaller
7045  {
7046  if(DepartureTimeSet)
7047  {
7048  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7049  }
7050  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7051  {
7052  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7053  }
7054  else
7055  {
7056  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7057  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7058  Count++; // because there are 2 entries
7059  }
7060  }
7061  }
7062  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7063  {
7064  AnsiString TrainLoc = "";
7065  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7066  {
7067  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7068  }
7069  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7070  {
7071  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7072  }
7073  else
7074  {
7075  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7076  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7077  Count++; // because there are 2 entries
7078  }
7079  }
7080  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7081  {
7082  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7083  }
7084  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7085  {
7086  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7087  }
7088  else if(Ptr->FormatType == PassTime) // new
7089  {
7090  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7091  }
7092  else if(Ptr->Command == "Fns")
7093  {
7094  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7095  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7096  PartStr = CheckNewServiceDepartureTime(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
7097  }
7098  else if(Ptr->Command == "F-nshs")
7099  {
7100  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7101  Ptr->LocationName;
7102  PartStr = CheckNewServiceDepartureTime(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7103  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7104  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7105  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7106  }
7107  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7108  {
7109  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7110  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7111  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7112  PartStr = CheckNewServiceDepartureTime(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7113  }
7114  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7115  {
7116  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7117  +" at " + Ptr->LocationName;
7118  PartStr = CheckNewServiceDepartureTime(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7119  }
7120  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7121  {
7122  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7123  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7124  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7125  PartStr = CheckNewServiceDepartureTime(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7126  }
7127  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7128  {
7129  PartStr = "Terminate at " + Ptr->LocationName;
7130  }
7131  else if(Ptr->Command == "Frh")
7132  {
7133  PartStr = "Terminate at " + Ptr->LocationName;
7134  }
7135  else if(Ptr->Command == "Fer")
7136  {
7137  AnsiString AllowedExits = "";
7138  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7139  }
7140  else if(Ptr->Command == "Fjo")
7141  {
7142  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7143  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7144  }
7145  else if(Ptr->Command == "jbo")
7146  {
7147  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7148  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7149  }
7150  else if(Ptr->Command == "fsp")
7151  {
7152  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7153  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7154  }
7155  else if(Ptr->Command == "rsp")
7156  {
7157  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7158  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7159  }
7160  else if(Ptr->Command == "cdt")
7161  {
7162  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7163  }
7164  if(RetStr != "")
7165  {
7166  RetStr = RetStr + '\n' + PartStr;
7167  }
7168  else
7169  {
7170  RetStr = PartStr;
7171  }
7172  FirstPass = false;
7173  Count++;
7174  }
7175  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7176  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7177  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7178  // forward as anyone should wish to see without looking at the full timetable
7179  if(TimetableFinished)
7180  {
7181  if(TrainMode == Timetable)
7182  {
7183  RetStr = "Timetable finished";
7184  }
7185  else
7186  {
7187  RetStr = "No timetable";
7188  }
7189  }
7190  Utilities->CallLogPop(1125);
7191  return(RetStr);
7192 }
7193 
7194 // ---------------------------------------------------------------------------
7195 
7196 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7197 {
7198  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7199  Utilities->SaveFileString(OutFile, HeadCode);
7202  Utilities->SaveFileInt(OutFile, StartSpeed);
7205  Utilities->SaveFileInt(OutFile, RepeatNumber);
7208  Utilities->SaveFileInt(OutFile, Mass);
7211  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7218  Utilities->SaveFileDouble(OutFile, BrakeRate);
7222  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7223  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7224  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7225  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7226  Utilities->SaveFileDouble(OutFile, double(TRSTime));
7227  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
7231  Utilities->SaveFileInt(OutFile, (short)TrainMode);
7236  Utilities->SaveFileBool(OutFile, Derailed);
7238  Utilities->SaveFileBool(OutFile, Crashed);
7245  Utilities->SaveFileBool(OutFile, NotInService);
7246  Utilities->SaveFileBool(OutFile, Plotted);
7247  Utilities->SaveFileBool(OutFile, TrainGone);
7248  Utilities->SaveFileBool(OutFile, SPADFlag);
7250  Utilities->SaveFileInt(OutFile, HOffset[0]);
7251  Utilities->SaveFileInt(OutFile, HOffset[1]);
7252  Utilities->SaveFileInt(OutFile, HOffset[2]);
7253  Utilities->SaveFileInt(OutFile, HOffset[3]);
7254  Utilities->SaveFileInt(OutFile, VOffset[0]);
7255  Utilities->SaveFileInt(OutFile, VOffset[1]);
7256  Utilities->SaveFileInt(OutFile, VOffset[2]);
7257  Utilities->SaveFileInt(OutFile, VOffset[3]);
7258  Utilities->SaveFileInt(OutFile, PlotElement[0]);
7259  Utilities->SaveFileInt(OutFile, PlotElement[1]);
7260  Utilities->SaveFileInt(OutFile, PlotElement[2]);
7261  Utilities->SaveFileInt(OutFile, PlotElement[3]);
7262  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
7263  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
7264  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
7265  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
7267  Utilities->SaveFileInt(OutFile, (short)Straddle);
7268  Utilities->SaveFileInt(OutFile, NextTrainID);
7269  Utilities->SaveFileInt(OutFile, TrainID);
7270  Utilities->SaveFileInt(OutFile, LeadElement);
7271  Utilities->SaveFileInt(OutFile, LeadEntryPos);
7272  Utilities->SaveFileInt(OutFile, LeadExitPos);
7273  Utilities->SaveFileInt(OutFile, MidElement);
7274  Utilities->SaveFileInt(OutFile, MidEntryPos);
7275  Utilities->SaveFileInt(OutFile, MidExitPos);
7276  Utilities->SaveFileInt(OutFile, LagElement);
7277  Utilities->SaveFileInt(OutFile, LagEntryPos);
7278  Utilities->SaveFileInt(OutFile, LagExitPos);
7279  int ColourNumber;
7280 
7282  {
7283  ColourNumber = 0;
7284  }
7286  {
7287  ColourNumber = 1;
7288  }
7290  {
7291  ColourNumber = 2;
7292  }
7294  {
7295  ColourNumber = 3;
7296  }
7298  {
7299  ColourNumber = 4;
7300  }
7302  {
7303  ColourNumber = 5;
7304  }
7306  {
7307  ColourNumber = 6;
7308  }
7310  {
7311  ColourNumber = 7;
7312  }
7314  {
7315  ColourNumber = 8;
7316  }
7318  {
7319  ColourNumber = 9;
7320  }
7322  {
7323  ColourNumber = 10;
7324  }
7326  {
7327  ColourNumber = 11;
7328  }
7330  {
7331  ColourNumber = 12;
7332  }
7333  else if(BackgroundColour == clTRSBackground)
7334  {
7335  ColourNumber = 13;
7336  }
7338  {
7339  ColourNumber = 14; // added at v2.4.0
7340  }
7341  Utilities->SaveFileInt(OutFile, ColourNumber);
7342 
7343  // additional data
7344  bool ForwardHeadCode;
7345 
7346  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
7347  {
7348  ForwardHeadCode = true;
7349  }
7350  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
7351  else
7352  {
7353  ForwardHeadCode = false;
7354  }
7355  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
7356 
7357  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
7358 
7359  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
7360  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
7361 
7362  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
7363  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
7364  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
7365  // so use the last asterisk position for this - 0 for false & 1 for true
7366  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
7367  AnsiString Marker;
7368 
7370  {
7371  Marker = "*****1";
7372  }
7373  else
7374  {
7375  Marker = "*****0";
7376  }
7377  if(RestoreTimetableLocation == "")
7378  {
7379  Utilities->SaveFileString(OutFile, Marker);
7380  }
7381  else
7382  {
7383  AnsiString CombinedString = RestoreTimetableLocation + Marker;
7384  Utilities->SaveFileString(OutFile, CombinedString);
7385  // RestoreTimetableLocation + marker
7386  }
7387  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
7388  Utilities->CallLogPop(1457);
7389 }
7390 
7391 // ---------------------------------------------------------------------------
7392 
7393 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
7394 {
7395  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
7396  HeadCode = Utilities->LoadFileString(InFile);
7399  StartSpeed = Utilities->LoadFileInt(InFile);
7401  if(SignallerMaxSpeed < 10)
7402  {
7403  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
7404  }
7406  RepeatNumber = Utilities->LoadFileInt(InFile);
7409  Mass = Utilities->LoadFileInt(InFile);
7412  {
7414  }
7415  // above added at v2.1.0 for legacy session files where value may not have been limited
7417  EntrySpeed = Utilities->LoadFileDouble(InFile);
7421  if(TimetableMaxRunningSpeed < 10)
7422  {
7423  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7424  }
7426  if(MaxRunningSpeed < 10)
7427  {
7428  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7429  }
7432  BrakeRate = Utilities->LoadFileDouble(InFile);
7436  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
7437  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
7438  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
7439  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
7440  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
7441  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
7450  Derailed = Utilities->LoadFileBool(InFile);
7452  Crashed = Utilities->LoadFileBool(InFile);
7459  NotInService = Utilities->LoadFileBool(InFile);
7460  Plotted = Utilities->LoadFileBool(InFile);
7461  TrainGone = Utilities->LoadFileBool(InFile);
7462  SPADFlag = Utilities->LoadFileBool(InFile);
7464  HOffset[0] = Utilities->LoadFileInt(InFile);
7465  HOffset[1] = Utilities->LoadFileInt(InFile);
7466  HOffset[2] = Utilities->LoadFileInt(InFile);
7467  HOffset[3] = Utilities->LoadFileInt(InFile);
7468  VOffset[0] = Utilities->LoadFileInt(InFile);
7469  VOffset[1] = Utilities->LoadFileInt(InFile);
7470  VOffset[2] = Utilities->LoadFileInt(InFile);
7471  VOffset[3] = Utilities->LoadFileInt(InFile);
7472  PlotElement[0] = Utilities->LoadFileInt(InFile);
7473  PlotElement[1] = Utilities->LoadFileInt(InFile);
7474  PlotElement[2] = Utilities->LoadFileInt(InFile);
7475  PlotElement[3] = Utilities->LoadFileInt(InFile);
7476  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7477  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7478  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7479  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7481  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7482  NextTrainID = Utilities->LoadFileInt(InFile);
7483  // will be same for all but best to save all anyway
7484  TrainID = Utilities->LoadFileInt(InFile);
7485  LeadElement = Utilities->LoadFileInt(InFile);
7486  LeadEntryPos = Utilities->LoadFileInt(InFile);
7487  LeadExitPos = Utilities->LoadFileInt(InFile);
7488  MidElement = Utilities->LoadFileInt(InFile);
7489  MidEntryPos = Utilities->LoadFileInt(InFile);
7490  MidExitPos = Utilities->LoadFileInt(InFile);
7491  LagElement = Utilities->LoadFileInt(InFile);
7492  LagEntryPos = Utilities->LoadFileInt(InFile);
7493  LagExitPos = Utilities->LoadFileInt(InFile);
7494  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7495 
7496  if(ColourNumber == 0)
7497  {
7499  }
7500  else if(ColourNumber == 1)
7501  {
7503  }
7504  else if(ColourNumber == 2)
7505  {
7507  }
7508  else if(ColourNumber == 3)
7509  {
7511  }
7512  else if(ColourNumber == 4)
7513  {
7515  }
7516  else if(ColourNumber == 5)
7517  {
7519  }
7520  else if(ColourNumber == 6)
7521  {
7523  }
7524  else if(ColourNumber == 7)
7525  {
7527  }
7528  else if(ColourNumber == 8)
7529  {
7531  }
7532  else if(ColourNumber == 9)
7533  {
7535  }
7536  else if(ColourNumber == 10)
7537  {
7539  }
7540  else if(ColourNumber == 11)
7541  {
7543  }
7544  else if(ColourNumber == 12)
7545  {
7547  }
7548  else if(ColourNumber == 13)
7549  {
7551  }
7552  else if(ColourNumber == 14)
7553  {
7554  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7555 
7556  }
7557  // additional data
7559  // sets the BackgroundColour to the loaded value
7560  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7561 
7562  if(ForwardHeadCode)
7563  {
7564  for(int x = 0; x < 4; x++)
7565  {
7567  }
7568  }
7569  else
7570  {
7571  for(int x = 0; x < 4; x++)
7572  {
7573  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7574  }
7575  }
7576  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7577  if(TrainMode == Timetable)
7578  {
7579  if(Crashed)
7580  {
7582  }
7583  else
7584  {
7586  }
7587  }
7588  else
7589  {
7591  }
7593  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7594  if(Straddle == LeadMid)
7595  {
7596  if(LeadElement > -1)
7597  {
7599  }
7600  if(LeadElement > -1)
7601  {
7603  }
7604  if(MidElement > -1)
7605  {
7607  }
7608  if(MidElement > -1)
7609  {
7611  }
7612  }
7613  else if(Straddle == LeadMidLag)
7614  {
7615  if(LeadElement > -1)
7616  {
7618  }
7619  if(MidElement > -1)
7620  {
7622  }
7623  if(MidElement > -1)
7624  {
7626  }
7627  if(LagElement > -1)
7628  {
7630  }
7631  }
7632  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7633 
7634  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7635  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7636 
7637  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7638 
7639  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7640  if(LeadElement > -1)
7641  // need to include this in case train exiting & no lead element
7642  {
7644  {
7645  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7646  }
7647  }
7648  AValue = sqrt(2 * PowerAtRail / Mass);
7649 
7650  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7651 
7652  // possible RestoreTimetableLocation + Marker, where Marker is
7653  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7654  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7655  // added at beta v0.2e
7656  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7657  // name not allowed to include the '*' character
7658  {
7659  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7660  bool GiveMessagesFalse = false;
7661  bool CheckLocationsExistInRailwayTrue = true;
7662  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7663  {
7664  // otherwise take no action
7665  RestoreTimetableLocation = Location;
7666  }
7667  }
7668  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7669 
7670  StoppedWithoutPower = false;
7671  if(Marker[6] == '1')
7672  {
7673  StoppedWithoutPower = true;
7674  }
7675  Utilities->CallLogPop(1458);
7676 }
7677 
7678 // ---------------------------------------------------------------------------
7679 
7680 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7681 {
7683  {
7684  return(false); // HeadCode
7685 
7686  }
7687  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7688  {
7689  return(false); // RearStartElement
7690 
7691  }
7692  if(!Utilities->CheckFileInt(InFile, 0, 3))
7693  {
7694  return(false); // RearStartExitPos
7695 
7696  }
7697  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7698  {
7699  return(false); // StartSpeed
7700 
7701  }
7702  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7703  {
7704  return(false); // SignallerMaxSpeed
7705 
7706  }
7707  if(!Utilities->CheckFileBool(InFile))
7708  {
7709  return(false); // HoldAtLocationInTTMode
7710 
7711  }
7712  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7713  {
7714  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
7715 
7716  }
7717  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7718  {
7719  return(false); // IncrementalMinutes (max 96 x 60)
7720 
7721  }
7722  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7723  {
7724  return(false); // IncrementalDigits
7725 
7726  }
7727  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7728  {
7729  return(false); // Mass
7730 
7731  }
7732  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7733  {
7734  return(false);
7735  }
7736  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7737  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7738  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7739  {
7740  return(false); // FrontElementLength
7741 
7742  }
7743  if(!Utilities->CheckFileDouble(InFile))
7744  {
7745  return(false); // EntrySpeed
7746 
7747  }
7748  if(!Utilities->CheckFileDouble(InFile))
7749  {
7750  return(false); // ExitSpeedHalf
7751 
7752  }
7753  if(!Utilities->CheckFileDouble(InFile))
7754  {
7755  return(false); // ExitSpeedFull
7756 
7757  }
7758  if(!Utilities->CheckFileDouble(InFile))
7759  {
7760  return(false); // TimetableMaxRunningSpeed
7761 
7762  }
7763  if(!Utilities->CheckFileDouble(InFile))
7764  {
7765  return(false); // MaxRunningSpeed
7766 
7767  }
7768  if(!Utilities->CheckFileDouble(InFile))
7769  {
7770  return(false); // MaxExitSpeed
7771 
7772  }
7773  if(!Utilities->CheckFileDouble(InFile))
7774  {
7775  return(false); // MaxBrakeRate
7776 
7777  }
7778  if(!Utilities->CheckFileDouble(InFile))
7779  {
7780  return(false); // BrakeRate
7781 
7782  }
7783  if(!Utilities->CheckFileDouble(InFile))
7784  {
7785  return(false); // PowerAtRail
7786 
7787  }
7788  if(!Utilities->CheckFileBool(InFile))
7789  {
7790  return(false); // FirstHalfMove
7791 
7792  }
7793  if(!Utilities->CheckFileBool(InFile))
7794  {
7795  return(false); // OneLengthAccelDecel
7796 
7797  }
7798  if(!Utilities->CheckFileDouble(InFile))
7799  {
7800  return(false); // double(EntryTime)
7801 
7802  }
7803  if(!Utilities->CheckFileDouble(InFile))
7804  {
7805  return(false); // double(ExitTimeHalf)
7806 
7807  }
7808  if(!Utilities->CheckFileDouble(InFile))
7809  {
7810  return(false); // double(ExitTimeFull)
7811 
7812  }
7813  if(!Utilities->CheckFileDouble(InFile))
7814  {
7815  return(false); // double(ReleaseTime)
7816 
7817  }
7818  if(!Utilities->CheckFileDouble(InFile))
7819  {
7820  return(false); // double(TRSTime)
7821 
7822  }
7823  if(!Utilities->CheckFileDouble(InFile))
7824  {
7825  return(false); // double(LastActionTime)
7826 
7827  }
7828  if(!Utilities->CheckFileBool(InFile))
7829  {
7830  return(false); // CallingOnFlag
7831 
7832  }
7833  if(!Utilities->CheckFileBool(InFile))
7834  {
7835  return(false); // BeingCalledOn
7836 
7837  }
7838  if(!Utilities->CheckFileBool(InFile))
7839  {
7840  return(false); // DepartureTimeSet
7841 
7842  }
7843  if(!Utilities->CheckFileInt(InFile, 0, 2))
7844  {
7845  return(false); // (short)TrainMode
7846 
7847  }
7848  if(!Utilities->CheckFileBool(InFile))
7849  {
7850  return(false); // TimetableFinished
7851 
7852  }
7853  if(!Utilities->CheckFileBool(InFile))
7854  {
7855  return(false); // LastActionDelayFlag
7856 
7857  }
7858  if(!Utilities->CheckFileBool(InFile))
7859  {
7860  return(false); // SignallerRemoved
7861 
7862  }
7863  if(!Utilities->CheckFileBool(InFile))
7864  {
7865  return(false); // TerminatedMessageSent
7866 
7867  }
7868  if(!Utilities->CheckFileBool(InFile))
7869  {
7870  return(false); // Derailed
7871 
7872  }
7873  if(!Utilities->CheckFileBool(InFile))
7874  {
7875  return(false); // DerailPending
7876 
7877  }
7878  if(!Utilities->CheckFileBool(InFile))
7879  {
7880  return(false); // Crashed
7881 
7882  }
7883  if(!Utilities->CheckFileBool(InFile))
7884  {
7885  return(false); // StoppedAtBuffers
7886 
7887  }
7888  if(!Utilities->CheckFileBool(InFile))
7889  {
7890  return(false); // StoppedAtSignal
7891 
7892  }
7893  if(!Utilities->CheckFileBool(InFile))
7894  {
7895  return(false); // StoppedAtLocation
7896 
7897  }
7898  if(!Utilities->CheckFileBool(InFile))
7899  {
7900  return(false); // SignallerStopped
7901 
7902  }
7903  if(!Utilities->CheckFileBool(InFile))
7904  {
7905  return(false); // StoppedAfterSPAD
7906 
7907  }
7908  if(!Utilities->CheckFileBool(InFile))
7909  {
7910  return(false); // StoppedForTrainInFront
7911 
7912  }
7913  if(!Utilities->CheckFileBool(InFile))
7914  {
7915  return(false); // NotInService
7916 
7917  }
7918  if(!Utilities->CheckFileBool(InFile))
7919  {
7920  return(false); // Plotted
7921 
7922  }
7923  if(!Utilities->CheckFileBool(InFile))
7924  {
7925  return(false); // TrainGone
7926 
7927  }
7928  if(!Utilities->CheckFileBool(InFile))
7929  {
7930  return(false); // SPADFlag
7931 
7932  }
7933  if(!Utilities->CheckFileBool(InFile))
7934  {
7935  return(false); // TimeTimeLocArrived
7936 
7937  }
7938  if(!Utilities->CheckFileInt(InFile, 0, 15))
7939  {
7940  return(false); // HOffset[0]
7941 
7942  }
7943  if(!Utilities->CheckFileInt(InFile, 0, 15))
7944  {
7945  return(false); // HOffset[1]
7946 
7947  }
7948  if(!Utilities->CheckFileInt(InFile, 0, 15))
7949  {
7950  return(false); // HOffset[2]
7951 
7952  }
7953  if(!Utilities->CheckFileInt(InFile, 0, 15))
7954  {
7955  return(false); // HOffset[3]
7956 
7957  }
7958  if(!Utilities->CheckFileInt(InFile, 0, 15))
7959  {
7960  return(false); // VOffset[0]
7961 
7962  }
7963  if(!Utilities->CheckFileInt(InFile, 0, 15))
7964  {
7965  return(false); // VOffset[1]
7966 
7967  }
7968  if(!Utilities->CheckFileInt(InFile, 0, 15))
7969  {
7970  return(false); // VOffset[2]
7971 
7972  }
7973  if(!Utilities->CheckFileInt(InFile, 0, 15))
7974  {
7975  return(false); // VOffset[3]
7976 
7977  }
7978  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7979  {
7980  return(false); // PlotElement[0]
7981 
7982  }
7983  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7984  {
7985  return(false); // PlotElement[1]
7986 
7987  }
7988  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7989  {
7990  return(false); // PlotElement[2]
7991 
7992  }
7993  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7994  {
7995  return(false); // PlotElement[3]
7996 
7997  }
7998  if(!Utilities->CheckFileInt(InFile, 0, 3))
7999  {
8000  return(false); // PlotEntryPos[0]
8001 
8002  }
8003  if(!Utilities->CheckFileInt(InFile, 0, 3))
8004  {
8005  return(false); // PlotEntryPos[1]
8006 
8007  }
8008  if(!Utilities->CheckFileInt(InFile, 0, 3))
8009  {
8010  return(false); // PlotEntryPos[2]
8011 
8012  }
8013  if(!Utilities->CheckFileInt(InFile, 0, 3))
8014  {
8015  return(false); // PlotEntryPos[3]
8016 
8017  }
8018  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8019  {
8020  return(false); // TrainCrashedInto
8021 
8022  }
8023  if(!Utilities->CheckFileInt(InFile, 0, 2))
8024  {
8025  return(false); // (short)Straddle
8026 
8027  }
8028  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8029  {
8030  return(false); // NextTrainID
8031 
8032  }
8033  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8034  {
8035  return(false); // TrainID
8036 
8037  }
8038  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8039  {
8040  return(false); // LeadElement
8041 
8042  }
8043  if(!Utilities->CheckFileInt(InFile, 0, 3))
8044  {
8045  return(false); // LeadEntryPos
8046 
8047  }
8048  if(!Utilities->CheckFileInt(InFile, 0, 3))
8049  {
8050  return(false); // LeadExitPos
8051 
8052  }
8053  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8054  {
8055  return(false); // MidElement
8056 
8057  }
8058  if(!Utilities->CheckFileInt(InFile, 0, 3))
8059  {
8060  return(false); // MidEntryPos
8061 
8062  }
8063  if(!Utilities->CheckFileInt(InFile, 0, 3))
8064  {
8065  return(false); // MidExitPos
8066 
8067  }
8068  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8069  {
8070  return(false); // LagElement
8071 
8072  }
8073  if(!Utilities->CheckFileInt(InFile, 0, 3))
8074  {
8075  return(false); // LagEntryPos
8076 
8077  }
8078  if(!Utilities->CheckFileInt(InFile, 0, 3))
8079  {
8080  return(false); // LagExitPos
8081 
8082  }
8083  if(!Utilities->CheckFileInt(InFile, 0, 14))
8084  {
8085  return(false);
8086  }
8087  // Background colour number //14 is failed colour at v2.4.0
8088  if(!Utilities->CheckFileBool(InFile))
8089  {
8090  return(false); // ForwardHeadCode
8091 
8092  }
8093  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8094  {
8095  return(false); // TrainDataEntryValue
8096 
8097  }
8098  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8099  {
8100  return(false); // ActionVectorEntryValue
8101 
8102  }
8104  {
8105  return(false); // End of train marker + possible RestoreTimetableLocation
8106 
8107  }
8108  // and StoppedWithoutPower flag
8109  return(true);
8110 }
8111 
8112 // ---------------------------------------------------------------------------
8113 
8114 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8115 {
8116  // order below reflects significance so earlier shows first, as may have more than one flag set
8117  // only plot flashing trains when Flash is true
8118 
8119 /*
8120  clCrashedBackground (TColor)0x0000FF red
8121  clDerailedBackground (TColor)0x0000FF red
8122  clSPADBackground (TColor)0x00FFFF yellow
8123  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8124  clCallOnBackground (TColor)0xFF33FF light magenta
8125  clSignalStopBackground (TColor)0x00FF66 green
8126  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8127  clStationStopBackground (TColor)0xCCFFCC pale green
8128  clTRSBackground (TColor)0xFFCCFF light pink
8129  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8130  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8131  clSignallerStopped (TColor)0x99CCFF caramel
8132  clNormalBackground (TColor)0xCCCCCC grey
8133 */
8134 
8135  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8136  bool HideFlashingTrain = true;
8137  // hide it when Flash false so it blinks on and off
8138  // if don't hide it it stays displayed all the time
8139  Graphics::TBitmap *SmallTrainBitmap;
8140 
8141  // NB ensure retain same order as zoomed in order so colours correspond
8143  {
8144  TrainController->CrashWarning = true;
8145  SmallTrainBitmap = RailGraphics->smRed;
8146  }
8148  {
8150  SmallTrainBitmap = RailGraphics->smRed;
8151  }
8153  {
8154  TrainController->SPADWarning = true;
8155  SmallTrainBitmap = RailGraphics->smYellow;
8156  }
8158  {
8160  SmallTrainBitmap = RailGraphics->smOrange;
8161  }
8163  {
8165  SmallTrainBitmap = RailGraphics->smMagenta;
8166  }
8168  {
8170  SmallTrainBitmap = RailGraphics->smBrightGreen;
8171  }
8173  {
8175  SmallTrainBitmap = RailGraphics->smCyan;
8176  }
8178  {
8179  SmallTrainBitmap = RailGraphics->smPaleGreen;
8180  HideFlashingTrain = false;
8181  }
8183  {
8184  SmallTrainBitmap = RailGraphics->smCyan;
8185  HideFlashingTrain = false;
8186  }
8188  {
8189  SmallTrainBitmap = RailGraphics->smLightBlue;
8190  HideFlashingTrain = false;
8191  }
8193  {
8194  SmallTrainBitmap = RailGraphics->smCaramel;
8195  HideFlashingTrain = false;
8196  }
8197  else
8198  {
8199  SmallTrainBitmap = RailGraphics->smBlack; // moving
8200  HideFlashingTrain = false;
8201  }
8202  // now plot the new train
8203  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8204  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8205  {
8206  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8207  }
8208  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8209  {
8210  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8211  }
8212  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8213  {
8214  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8215  }
8219  Utilities->CallLogPop(1459);
8220 }
8221 
8222 // ---------------------------------------------------------------------------
8223 
8225 {
8226  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
8227  if(!Display->ZoomOutFlag)
8228  {
8229  Utilities->CallLogPop(1304);
8230  return;
8231  }
8232  for(int y = 0; y < 3; y++)
8233  {
8234  if(OldZoomOutElement[y] > -1)
8235  {
8236  bool FoundFlag = false;
8237  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
8238  TTrackElement IATElement1, IATElement2;
8239  // default elements to begin with
8240  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
8241  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
8242  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
8243  if(FoundFlag)
8244  {
8245  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
8246  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
8247  if(IMPair.first != IMPair.second)
8248  {
8249  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
8250  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
8251  }
8252  }
8253  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
8254  }
8255  }
8256  Utilities->CallLogPop(1305);
8257 }
8258 
8259 // ---------------------------------------------------------------------------
8260 
8261 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
8262 {
8263  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
8264  LocationName = "";
8265  if(!StoppedAtLocation)
8266  {
8267  Utilities->CallLogPop(1398);
8268  return(false);
8269  }
8270  if(LeadElement > -1)
8271  {
8273  }
8274  if((LocationName == "") && (MidElement > -1))
8275  {
8276  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
8277  }
8278  if((LocationName == "") && (LagElement > -1))
8279  {
8280  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
8281  }
8282  if(LocationName == "")
8283  {
8284  throw Exception("Error - Location name not set in TrainAtLocation");
8285  }
8286  Utilities->CallLogPop(1399);
8287  return(true);
8288 }
8289 
8290 // ---------------------------------------------------------------------------
8291 
8292 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
8293 {
8294  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
8295  for(int x = 0; x < 4; x++)
8296  {
8297  PlotTrainGraphic(7, x, Disp);
8298  }
8299  Utilities->CallLogPop(647);
8300 }
8301 
8302 // ---------------------------------------------------------------------------
8303 
8304 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
8305 {
8306  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
8307  for(int x = 0; x < 4; x++)
8308  {
8309  if(PlotElement[x] > -1)
8310  {
8311  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
8312  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
8313  }
8314  }
8315  Utilities->CallLogPop(1708);
8316 }
8317 
8318 // ---------------------------------------------------------------------------
8319 
8320 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
8321 {
8322  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
8323  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
8324  AnsiString(LinkNumber) + "," + HeadCode);
8325 
8326 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
8327  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
8328  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
8329 */
8330 
8331  // note that MidElement always fully occupied
8332  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
8333  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
8334  {
8335  Utilities->CallLogPop(2005);
8336  return(true);
8337  }
8338  if(Straddle == LeadMid)
8339  {
8340  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
8341  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
8342  {
8343  Utilities->CallLogPop(2006);
8344  return(true);
8345  }
8346  }
8347  else if(Straddle == LeadMidLag)
8348  {
8349  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
8350  // only interested in LeadEntryPos as train not occupying ExitPos yet
8351  {
8352  Utilities->CallLogPop(2007);
8353  return(true);
8354  }
8355  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
8356  // only interested in LagExitPos as train has left EntryPos
8357  {
8358  Utilities->CallLogPop(2008);
8359  return(true);
8360  }
8361  }
8362  Utilities->CallLogPop(2009);
8363  return(false);
8364 }
8365 
8366 // ---------------------------------------------------------------------------
8367 
8368 float TTrain::CalcTimeToAct(int Caller) // only called for running trains
8373 {
8374  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
8375  int DistanceToRedSignal = 0;
8376  float TimeToAct = 0;
8377  float MinsEarly = 0; //added at v2.6.1
8378  TDateTime DepartureTime; //added at v2.6.1
8379 
8380  if(TrainFailed)
8381  {
8382  Utilities->CallLogPop(2147);
8383  return(0); // time to act now
8384  }
8385  if(SignallerStopped)
8386  {
8387  Utilities->CallLogPop(2080);
8388  return(-1);
8389  }
8390  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
8391  {
8392  Utilities->CallLogPop(2266);
8393  return(-1);
8394  }
8395  if(!Stopped() || StoppedAtLocation)
8396  {
8397  // calc distance to next red signal but check for continuation exit
8398  if(LeadElement == -1)
8399  // if -1 it's on an end element so no action needed
8400  {
8401  Utilities->CallLogPop(2075);
8402  return(-1);
8403  }
8404  else
8405  {
8406  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
8407  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
8408  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
8409 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
8410  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
8411  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
8412  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
8413  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
8414  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
8415  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
8416  before the train has stopped the current station is still recognised as a future stop.
8417  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
8418  becomes the signal, and the time to act indication becomes 'NOW'.
8419 */
8420  {
8421  FirstPosToBeMeasured = LeadElement;
8422  FirstEntryPos = LeadEntryPos;
8423  }
8424  float CurrentStopTime; // set to 0 at start of function
8425  float LaterStopTime; // set to 0 at start of function
8426  float RecoverableTime; // set to 0 at start of function
8427  int AvTrackSpeed; // set to zero at start of function
8428  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
8429  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
8430  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
8431  if(DistanceToRedSignal == -1) // -1 for no action needed
8432  {
8433  Utilities->CallLogPop(2076);
8434  return(-1);
8435  }
8436 /* Have MinsDelayed; pos or neg,
8437  CurrentStopTime; pos or zero
8438  LaterStopTime; pos or zero
8439  RecoverableTime; pos or zero
8440 
8441  & from these calculate TotalStopTime. noting that:
8442  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
8443  RecoverableTime always < LaterStopTime or both zero
8444  can't subtract more than RecoverableTime (MinsDelayed > 0)
8445  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
8446  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
8447  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
8448  if running early & stopped at location CurrentStopTime will automatically include the excess
8449 */
8450  float TimeToSubtract, TotalStopTime;
8451  if(MinsDelayed > RecoverableTime)
8452  {
8453  TimeToSubtract = RecoverableTime;
8454  }
8455  else
8456  {
8457  TimeToSubtract = MinsDelayed; // may be negative;
8458 
8459  }
8460  if((AvTrackSpeed > 0) && (DistanceToStationStop <= DistanceToRedSignal) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
8461  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
8462  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
8463  //next station stop
8464  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
8465  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
8466  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
8467  //first find departure time from the next stop
8468  {
8470  {
8472  }
8473  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
8474  {
8475  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
8476  {
8477  // must be a departure
8478  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
8479  }
8480  }
8481  MinsEarly = (double(DepartureTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8482  if(MinsEarly < 0)
8483  {
8484  MinsEarly = 0;
8485  }
8486  }
8487  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
8488  {
8489  if(CurrentStopTime > 0)
8490  {
8491  TotalStopTime = CurrentStopTime + LaterStopTime;
8492  }
8493  // stopped at loc, will depart on time
8494  else
8495  {
8496  TotalStopTime = LaterStopTime - MinsDelayed;
8497  }
8498  // not stopped, will depart on time at first later stop so add the delay
8499  }
8500  else if((MinsEarly > 0) && !Stopped()) //running early
8501  {
8502  TotalStopTime = LaterStopTime + MinsEarly;
8503  }
8504  else // on time or running late
8505  {
8506  if(LaterStopTime == 0)
8507  {
8508  TotalStopTime = CurrentStopTime;
8509  }
8510  // no later stops, if stopped now will depart as soon as possible,
8511  // if not stopped no stop times to add
8512  else
8513  {
8514  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
8515  }
8516  }
8517  if(AvTrackSpeed < 30)
8518  {
8519  AvTrackSpeed = 30;
8520  }
8521  int Speed = AvTrackSpeed;
8522  if(AvTrackSpeed > int(MaxRunningSpeed))
8523  {
8524  Speed = int(MaxRunningSpeed);
8525  }
8526  if(TrainMode == Signaller)
8527  {
8528  Speed = SignallerMaxSpeed;
8529  TotalStopTime = 0;
8530  }
8531  TimeToAct = TotalStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
8532  // accel & decel taken into account in
8533  // CalcDistanceToRedSignalandStopTime
8534  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8535  Utilities->CallLogPop(2079);
8536  return(TimeToAct);
8537  }
8538  }
8539  else // stopped not at location
8540  {
8542  {
8543  TimeToAct = 0.0;
8544  }
8545  // but if stopped at a signal & autosigs route after it then ok
8546  if(StoppedAtSignal)
8547  {
8548  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
8549  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
8550  int NextExitPos;
8551  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
8552  {
8553  if((NextEntryPos == 0) || (NextEntryPos == 2))
8554  // leading entry point
8555  {
8556  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
8557  {
8558  NextExitPos = 1;
8559  }
8560  else
8561  {
8562  NextExitPos = 3;
8563  }
8564  }
8565  else
8566  {
8567  NextExitPos = 0; // trailing entry point
8568  }
8569  }
8570  else
8571  {
8572  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
8573  }
8574  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
8575  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
8576  int RouteNumber; // holder for referenced value, not used
8577  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8578  {
8579  TimeToAct = -1;
8580  }
8581  }
8582  Utilities->CallLogPop(2074);
8583  return(TimeToAct);
8584  }
8585 }
8586 
8587 // ---------------------------------------------------------------------------
8588 
8590 {
8591  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
8592  if(LeadElement > -1)
8593  {
8595  {
8596  Utilities->CallLogPop(2148);
8597  return(true);
8598  }
8599  }
8600  if(MidElement > -1)
8601  {
8603  {
8604  Utilities->CallLogPop(2149);
8605  return(true);
8606  }
8607  }
8608  if(LagElement > -1)
8609  {
8611  {
8612  Utilities->CallLogPop(2150);
8613  return(true);
8614  }
8615  }
8616  Utilities->CallLogPop(2151);
8617  return(false);
8618 }
8619 
8620 // ---------------------------------------------------------------------------
8621 // TTrainController
8622 // ---------------------------------------------------------------------------
8623 
8625 {
8626  OnTimeArrivals = 0;
8627  LateArrivals = 0;
8628  EarlyArrivals = 0;
8629  OnTimePasses = 0;
8630  LatePasses = 0;
8631  EarlyPasses = 0;
8632  OnTimeDeps = 0;
8633  LateDeps = 0;
8634  MissedStops = 0;
8635  OtherMissedEvents = 0;
8636  UnexpectedExits = 0;
8637  NumFailures = 0;
8638  IncorrectExits = 0;
8639  SPADEvents = 0;
8640  SPADRisks = 0;
8641  CrashedTrains = 0;
8642  Derailments = 0;
8643  TotArrDepPass = 0;
8644  TotLateArrMins = 0;
8645  TotEarlyArrMins = 0;
8646  TotLatePassMins = 0;
8647  TotEarlyPassMins = 0;
8648  TotLateDepMins = 0;
8649  ExcessLCDownMins = 0;
8650  TTClockTime = 0; // added for v0.6
8652  // added at v1.3.0 to ensure false at start
8653  OpTimeToActUpdateCounter = 0; // new v2.2.0
8654  OpActionPanelVisible = false; // new v2.2.0
8655  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
8656  SSHigh = false;
8657  MRSHigh = false;
8658  MRSLow = false;
8659  MassHigh = false;
8660  BFHigh = false;
8661  BFLow = false;
8662  PwrHigh = false;
8663  SigSHigh = false;
8664  SigSLow = false;
8665  randomize();
8666  // to seed rand() & random() with a random number (see UpdateTrain)
8667 }
8668 
8669 // ---------------------------------------------------------------------------
8670 
8672 {
8673  for(unsigned int x = 0; x < TrainVector.size(); x++)
8674  {
8675  TrainVectorAt(32, x).DeleteTrain(4);
8676  }
8677  TrainVector.clear();
8678 }
8679 
8680 // ---------------------------------------------------------------------------
8681 
8682 void TTrainController::LogEvent(AnsiString Str)
8683 {
8684  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
8685 
8686  // restrict to last 1000 entries
8687  Utilities->EventLog.push_back(FullStr);
8688  if(Utilities->EventLog.size() > 1000)
8689  {
8690  Utilities->EventLog.pop_front();
8691  }
8692 }
8693 
8694 // ---------------------------------------------------------------------------
8695 
8697 {
8698  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
8699  bool ClockState = Utilities->Clock2Stopped;
8700 
8701  Utilities->Clock2Stopped = true;
8702  // new section dealing with Snt & Snt-sh additions
8703  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
8704  // clock tick after stops flashing
8706  {
8707  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8708  {
8709  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
8710  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
8711  TActionEventType EventType = NoEvent;
8712  if(AVEntry0.Command == "Snt")
8713  {
8714  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8715  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8716  int IncrementalMinutes = 0;
8717  int IncrementalDigits = 0;
8718  if(AVEntryLast.FormatType == Repeat)
8719  {
8720  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8721  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8722  }
8723  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8724  {
8725  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
8726  }
8727  // see above note
8728 
8729  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8730  {
8731  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
8732  if(TTOD.RunningEntry != NotStarted)
8733  {
8734  continue;
8735  }
8736  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
8737  {
8738  break; // all the rest will also be greater
8739  }
8740  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
8741  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8742  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
8743  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
8744  {
8745  TTOD.TrainID = TrainVector.back().TrainID;
8746  TTOD.RunningEntry = Running;
8747  }
8748  else if(EventType == FailTrainEntry)
8749  {
8750  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8751  }
8752  }
8753  }
8754  if(AVEntry0.Command == "Snt-sh")
8755  // just start this once, shuttle repeats take care of restarts
8756  {
8757  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8758  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8759  int IncrementalMinutes = 0;
8760  int IncrementalDigits = 0;
8761  if(AVEntryLast.FormatType == Repeat)
8762  {
8763  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8764  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8765  }
8766  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8767  {
8768  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
8769  }
8770  // see above note
8771  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
8772  if(TTOD.RunningEntry == NotStarted)
8773  {
8774  if(AVEntry0.EventTime <= TTClockTime)
8775  {
8776  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8777  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
8778  TDEntry.SignallerSpeed, false, EventType))
8779  // false for SignallerControl
8780  {
8781  TTOD.TrainID = TrainVector.back().TrainID;
8782  TTOD.RunningEntry = Running;
8783  }
8784  else if(EventType == FailTrainEntry)
8785  {
8786  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8787  }
8788  }
8789  }
8790  }
8791  }
8792  }
8793  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
8794  // iteration, next cycle will catch up with any other pending updates
8795  if(!TrainVector.empty())
8796  {
8797  TrainAdded = false;
8798  AllRoutes->CallonVector.clear();
8799  // this will be rebuilt during the calls to UpdateTrain
8800  for(unsigned int x = 0; x < TrainVector.size(); x++)
8801  {
8802  TrainVectorAt(33, x).UpdateTrain(0);
8803  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
8804  // added HasTrainGone() condition in v0.4c to prevent 2 trains both having TrainGone set
8805  // at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
8806  // iterates in reverse to erase the second train to have gone, but afterwards ReplotTrains iterates forwards and therefore replots the first train to
8807  // have gone and therefore sets the TrainIDOnElement value to the exited train, with nothing to reset it. Hovering the mouse over that element with
8808  // train information enabled causes an error because the track element thinks the train is still there, whereas it is missing from the TrainVector.
8809  {
8810  break;
8811  }
8812  }
8813  // set warning flags
8814  CrashWarning = false;
8815  DerailWarning = false;
8816  SPADWarning = false;
8817  CallOnWarning = false;
8818  SignalStopWarning = false;
8819  BufferAttentionWarning = false;
8820  TrainFailedWarning = false;
8821  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
8822  {
8823  TTrain &Train = TrainVectorAt(34, x);
8824  if(Train.Crashed)
8825  // can't use background colours for crashed & derailed because same colour
8826  {
8827  CrashWarning = true;
8828  }
8829  else if(Train.Derailed)
8830  // can't use background colours for crashed & derailed because same colour
8831  {
8832  DerailWarning = true;
8833  }
8834  else if(Train.BackgroundColour == clSPADBackground)
8835  // use colour as that changes as soon as passes signal
8836  {
8837  SPADWarning = true;
8838  }
8839  else if(Train.BackgroundColour == clTrainFailedBackground)
8840  {
8841  TrainFailedWarning = true;
8842  }
8843  else if(Train.BackgroundColour == clCallOnBackground)
8844  // use colour as also stopped at signal
8845  {
8846  CallOnWarning = true;
8847  }
8848  else if(Train.BackgroundColour == clSignalStopBackground)
8849  // use colour to distinguish from call-on
8850  {
8851  SignalStopWarning = true;
8852  }
8853  else if(Train.BackgroundColour == clBufferAttentionNeeded)
8854  // use colour to distinguish from ordinary buffer stop
8855  {
8856  BufferAttentionWarning = true;
8857  }
8858  if(Train.HasTrainGone())
8859  {
8860  AnsiString Loc = "";
8861  bool ElementFound = false;
8862  TTrackElement TE;
8863  if(Train.LagElement > -1)
8864  {
8865  TE = Track->TrackElementAt(531, Train.LagElement);
8866  ElementFound = true;
8867  }
8868  else if(Train.MidElement > -1)
8869  {
8870  TE = Track->TrackElementAt(779, Train.MidElement);
8871  ElementFound = true;
8872  }
8873  else if(Train.LeadElement > -1)
8874  {
8875  TE = Track->TrackElementAt(780, Train.LeadElement);
8876  ElementFound = true;
8877  }
8878  if(ElementFound)
8879  {
8880  if(TE.ActiveTrackElementName != "")
8881  {
8882  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8883  }
8884  else
8885  {
8886  Loc = "track element " + TE.ElementID;
8887  }
8888  }
8889  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
8890  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
8891  // need above first because may also have ActionVectorEntryPtr == "Fer"
8892  {
8893  Train.UnplotTrain(9);
8894  // added at v1.3.0 to reset signals after train removed from an autosigsroute
8896  {
8899  }
8900  // end of addition
8901  AllRoutes->RebuildRailwayFlag = true;
8902  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
8903  // correctly after a crash
8904  }
8905  else if(AVEntryPtr->Command == "Fer")
8906  {
8907  bool CorrectExit = false;
8908  if(!AVEntryPtr->ExitList.empty())
8909  {
8910  for(TExitListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
8911  {
8912  if(*ELIT == Train.LagElement)
8913  {
8914  CorrectExit = true;
8915  }
8916  }
8917  }
8918  if(CorrectExit)
8919  {
8920  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
8921  }
8922  else
8923  {
8924  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
8925  }
8926  }
8927  else
8928  {
8929  if(!AVEntryPtr->SignallerControl)
8930  {
8931  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
8932  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
8933  // -2 is marker for send messages for all remaining actions except Fer if present
8934  }
8935  else
8936  {
8937  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
8938  }
8939  }
8940  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
8941  Train.DeleteTrain(1);
8942  TrainVector.erase(TrainVector.begin() + x);
8943  ReplotTrains(1, Display);
8944  // to reset ElementIDs for remaining trains when have removed a train
8945  }
8946  }
8947  }
8948  else
8949  {
8950  // reset all flags in case last train removed with flag set
8951  CrashWarning = false;
8952  DerailWarning = false;
8953  SPADWarning = false;
8954  CallOnWarning = false;
8955  SignalStopWarning = false;
8956  BufferAttentionWarning = false;
8957  TrainFailedWarning = false;
8958  }
8959  // update OpTimeToActMultimap
8961  {
8963  // clears entries then adds values for running trains then for continuation entries
8964  }
8965  Utilities->Clock2Stopped = ClockState;
8966  Utilities->CallLogPop(723);
8967 }
8968 
8969 // ---------------------------------------------------------------------------
8971 {
8972  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
8973  if(!TrainVector.empty())
8974  {
8975  for(int x = TrainVector.size() - 1; x >= 0; x--)
8976  {
8977  TrainVectorAt(50, x).DeleteTrain(2);
8978  }
8979  TrainVector.clear();
8980  }
8981  if(!TrainDataVector.empty())
8982  {
8983  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8984  {
8985  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
8986  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8987  {
8988  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
8989  TOD.RunningEntry = NotStarted;
8990  TOD.TrainID = -1;
8991  TOD.EventReported = NoEvent;
8992  }
8993  }
8994  }
8995  Display->GetOutputLog1()->Caption = "";
8996  Display->GetOutputLog2()->Caption = "";
8997  Display->GetOutputLog3()->Caption = "";
8998  Display->GetOutputLog4()->Caption = "";
8999  Display->GetOutputLog5()->Caption = "";
9000  Display->GetOutputLog6()->Caption = "";
9001  Display->GetOutputLog7()->Caption = "";
9002  Display->GetOutputLog8()->Caption = "";
9003  Display->GetOutputLog9()->Caption = "";
9004  Display->GetOutputLog10()->Caption = "";
9005  Utilities->CallLogPop(1352);
9006 }
9007 
9008 // ---------------------------------------------------------------------------
9009 
9011 {
9012  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
9013  if(!TrainVector.empty())
9014  {
9015  for(unsigned int x = 0; x < TrainVector.size(); x++)
9016  {
9017  TrainVectorAt(51, x).PlotTrain(4, Disp);
9018  }
9019  }
9020  Utilities->CallLogPop(724);
9021 }
9022 
9023 // ---------------------------------------------------------------------------
9024 
9025 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
9026 {
9027  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
9028  if(!TrainVector.empty())
9029  {
9030  for(unsigned int x = 0; x < TrainVector.size(); x++)
9031  {
9032  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
9033  }
9034  }
9035  Utilities->CallLogPop(1707);
9036 }
9037 
9038 // ---------------------------------------------------------------------------
9039 
9041 {
9042  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
9043  if(!TrainVector.empty())
9044  {
9045  for(unsigned int x = 0; x < TrainVector.size(); x++)
9046  {
9047  TrainVectorAt(52, x).UnplotTrain(10);
9048  }
9049  }
9051  Utilities->CallLogPop(725);
9052 }
9053 
9054 // ---------------------------------------------------------------------------
9055 
9056 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
9057  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
9058  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
9059 {
9060  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
9061  "," + AnsiString(Mass) + "," + ModeStr);
9062  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
9063  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr + "," + HeadCode);
9064 
9065  int RearExitPos = -1;
9066 
9067  for(int x = 0; x < 4; x++)
9068  {
9069  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
9070  {
9071  RearExitPos = x;
9072  }
9073  }
9074  if(RearExitPos == -1)
9075  {
9076  throw Exception("Error, RearExit == -1 in AddTrain");
9077  }
9078  bool ReportFlag = true;
9079 
9080  // used to stop repeated messages from CheckStartAllowable when split failed
9081  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
9082  {
9083  ReportFlag = false;
9084  }
9085  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
9086  {
9087  // messages sent to performance log in CheckStartAllowable if ReportFlag true
9088  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
9089  Utilities->CallLogPop(938);
9090  return(false);
9091  }
9092  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
9093  TTrainMode TrainMode = NoMode;
9094 
9095  if(ModeStr == "Timetable")
9096  {
9097  TrainMode = Timetable;
9098  }
9099  // all else gives 'None', 'Signaller' set within program
9100 
9101  if(MaxRunningSpeed < 10)
9102  {
9103  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
9104  }
9105  if(SignallerSpeed < 10)
9106  {
9107  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
9108  }
9109  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
9110  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
9111 
9112  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
9113  // initialise here rather than in TTrain constructor as create trains
9114  // with Null TrainDataEntryPtr when loading session trains
9115  if(SignallerControl)
9116  {
9117  NewTrain->TimetableFinished = true;
9118  NewTrain->SignallerStoppingFlag = false;
9119  NewTrain->TrainMode = Signaller;
9120  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
9121  {
9122  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
9123  }
9125  }
9126  // deal with starting conditions:-
9127  // unlocated Snt: just report entry & advance pointer
9128  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
9129  // Sns doesn't need a new train
9130  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
9131  // covers all above located starts
9132  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
9133  // wouldn't have accepted the timetable
9134  {
9135  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
9136  // StoppedAtBuffers is set in UpdateTrain()
9137  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
9138  // buffer end must be ahead of train or would have failed start position check
9139  {
9140  NewTrain->StoppedAtLocation = true;
9141  NewTrain->PlotStartPosition(0);
9143  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9144  NewTrain->ActionVectorEntryPtr->Warning);
9145  if(!SignallerControl) // don't advance if SignalControlEntry
9146  {
9147  NewTrain->ActionVectorEntryPtr++;
9148  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
9149  }
9150  NewTrain->LastActionTime = TTClockTime;
9151  }
9152  // else a through station stop
9153  else
9154  {
9155  NewTrain->StoppedAtLocation = true;
9156  NewTrain->PlotStartPosition(10);
9158  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9159  NewTrain->ActionVectorEntryPtr->Warning);
9160  if(!SignallerControl) // don't advance if SignalControlEntry
9161  {
9162  NewTrain->ActionVectorEntryPtr++;
9163  }
9164  NewTrain->LastActionTime = TTClockTime;
9165  }
9166  }
9167  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
9168  {
9169  NewTrain->PlotStartPosition(11);
9170  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
9171  AnsiString Loc = "";
9172  if(TE.ActiveTrackElementName != "")
9173  {
9174  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9175  }
9176  else
9177  {
9178  Loc = "track element " + TE.ElementID;
9179  }
9180  if(TE.TrackType == Continuation)
9181  {
9182  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9183  }
9184  else
9185  {
9186  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9187  }
9188  if(!SignallerControl) // don't advance if SignalControlEntry
9189  {
9190  NewTrain->ActionVectorEntryPtr++;
9191  }
9192  NewTrain->LastActionTime = TTClockTime;
9193  // no need to set LastActionTime for an unlocated entry
9194  }
9195  // cancel a wrong-direction route if either element of train starts on one
9196  if(NewTrain->LeadElement > -1)
9197  {
9198  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
9199  }
9200  if(NewTrain->MidElement > -1)
9201  {
9202  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
9203  }
9204  // set signals for a right-direction autosigs route for either element of train on one
9205  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
9206  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
9207  int RouteNumber = -1;
9208  bool SignalsSet = false;
9209 
9210  if(NewTrain->LeadElement > -1)
9211  {
9212  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9213  {
9214  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9215  int RouteStartPosition;
9216  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9217  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
9218  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
9219  if(FirstPair.first == RouteNumber)
9220  {
9221  RouteStartPosition = FirstPair.second;
9222  }
9223  else if(SecondPair.first == RouteNumber)
9224  {
9225  RouteStartPosition = SecondPair.second;
9226  }
9227  else
9228  {
9229  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
9230  }
9231  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
9232  SignalsSet = true;
9233  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9234  }
9235  else if(RouteNumber > -1) // non-autosigsroute
9236  {
9237  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
9238  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9239  int FirstELinkPos = TempPDE.GetELinkPos();
9240  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
9241  {
9242  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9243  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
9244  }
9245  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
9246  {
9247  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9248  // remove the last element under LeadElement
9249  }
9250  AllRoutes->RebuildRailwayFlag = true;
9251  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9252  // now deal with a rear linked autosigs route
9253  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
9254  {
9255  int LinkedRouteNumber = -1;
9256  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
9257  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9258  {
9259  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
9260  // this is ok as here we are setting signals from the start of the route
9261  }
9262  }
9263  SignalsSet = true;
9264  }
9265  }
9266  if(NewTrain->MidElement > -1)
9267  // if entering at a continuation MidElement == -1
9268  {
9269  // this is included in case a train starts with LeadElement on no route and MidElement on a route
9270  if(!SignalsSet)
9271  {
9272  RouteNumber = -1;
9273  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9274  {
9275  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9276  int RouteStartPosition;
9277  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9278  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
9279  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
9280  if(FirstPair.first == RouteNumber)
9281  {
9282  RouteStartPosition = FirstPair.second;
9283  }
9284  else if(SecondPair.first == RouteNumber)
9285  {
9286  RouteStartPosition = SecondPair.second;
9287  }
9288  else
9289  {
9290  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
9291  }
9292  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
9293  SignalsSet = true;
9294  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9295  }
9296  else if(RouteNumber > -1) // non-autosigsroute
9297  {
9298  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
9299  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9300  int FirstELinkPos = TempPDE.GetELinkPos();
9301  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
9302  {
9303  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9304  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
9305  }
9306  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
9307  {
9308  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9309  // remove the last element under LeadElement
9310  }
9311  AllRoutes->RebuildRailwayFlag = true;
9312  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9313  // now deal with a rear linked autosigs route
9314  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
9315  {
9316  int LinkedRouteNumber = -1;
9317  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
9318  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9319  {
9320  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
9321  // this is ok as now we are setting signals from the start of the route
9322  }
9323  }
9324  }
9325  }
9326  }
9327  TrainVector.push_back(*NewTrain);
9328  Utilities->CallLogPop(731);
9329  return(true);
9330 }
9331 
9332 // ---------------------------------------------------------------------------
9333 
9334 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
9335 {
9336  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
9337  AnsiString(TrackVectorNumber));
9338  int VecPos = -1;
9339 
9340  for(unsigned int x = 0; x < TrainVector.size(); x++)
9341  {
9342  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
9343  {
9344  VecPos = x;
9345  }
9346  }
9347  if(VecPos == -1)
9348  {
9349  throw Exception("Error, VecPos not set in EntryPos");
9350  }
9351  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
9352  {
9353  Utilities->CallLogPop(734);
9354  return(TrainVectorAt(3, VecPos).LeadEntryPos);
9355  }
9356  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
9357  {
9358  Utilities->CallLogPop(735);
9359  return(TrainVectorAt(5, VecPos).MidEntryPos);
9360  }
9361  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
9362  {
9363  Utilities->CallLogPop(736);
9364  return(TrainVectorAt(7, VecPos).LagEntryPos);
9365  }
9366  Utilities->CallLogPop(737);
9367  return(-1);
9368 }
9369 
9370 // ---------------------------------------------------------------------------
9371 
9373 {
9374  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
9375  for(unsigned int x = 0; x < TrainVector.size(); x++)
9376  {
9377  if(TrainVectorAt(53, x).TrainID == TrainID)
9378  {
9379  Utilities->CallLogPop(738);
9380  return(TrainVectorAt(54, x));
9381  }
9382  }
9383  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
9384 }
9385 
9386 // ---------------------------------------------------------------------------
9387 
9388 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
9389 // return true if find the train (added at v2.4.0 as can select a removed train
9390 // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
9391 {
9392  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
9393  for(unsigned int x = 0; x < TrainVector.size(); x++)
9394  {
9395  if(TrainVectorAt(69, x).TrainID == TrainID)
9396  {
9397  Utilities->CallLogPop(2152);
9398  return(true);
9399  }
9400  }
9401  Utilities->CallLogPop(2153);
9402  return(false);
9403 }
9404 
9405 // ---------------------------------------------------------------------------
9406 
9407 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
9408 {
9409  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
9410  Utilities->Format96HHMMSS(Time));
9411  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
9412 
9413  Utilities->CallLogPop(2061);
9414  return(RepeatTime);
9415 }
9416 
9417 // ---------------------------------------------------------------------------
9418 
9419 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
9420 // Enter with Ptr pointing to first action to be listed (i.e. next action)
9421 {
9422  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
9423  AnsiString RetStr = "", PartStr = "";
9424  int Count = 0;
9425  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
9426 
9427  Ptr--; // because incremented at start of loop
9428  do
9429  {
9430  Ptr++;
9431  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
9432  {
9433  continue; // move past the starting entry
9434  }
9435  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
9436  {
9437  break;
9438  }
9439  if(Ptr->SignallerControl)
9440  {
9441  RetStr = "Train under signaller control";
9442  break;
9443  }
9444  if(Ptr->FormatType == TimeTimeLoc)
9445  {
9446  if(Ptr->ArrivalTime == Ptr->DepartureTime)
9447  {
9448  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
9449  }
9450  else
9451  {
9452  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
9453  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9454  Count++; // because there are 2 entries
9455  }
9456  }
9457  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
9458  {
9459  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
9460  }
9461  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
9462  {
9463  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9464  }
9465  else if(Ptr->FormatType == PassTime) // new
9466  {
9467  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
9468  }
9469  else if(Ptr->Command == "Fns")
9470  {
9471  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9472  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9473  PartStr = ControllerCheckNewServiceDepartureTime(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
9474  }
9475  else if(Ptr->Command == "F-nshs")
9476  {
9477  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9478  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
9479  PartStr = ControllerCheckNewServiceDepartureTime(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9480  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
9481  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
9482  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
9483  }
9484 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
9485  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9486  {
9487  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9488  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9489  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9490  PartStr = ControllerCheckNewServiceDepartureTime(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9491  }
9492  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9493  {
9494  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9495  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
9496  PartStr = ControllerCheckNewServiceDepartureTime(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9497  }
9498  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9499  {
9500  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9501  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9502  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9503  PartStr = ControllerCheckNewServiceDepartureTime(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9504  }
9505  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9506  {
9507  PartStr = "Terminate at " + Ptr->LocationName;
9508  }
9509  else if(Ptr->Command == "Frh")
9510  {
9511  PartStr = "Terminate at " + Ptr->LocationName;
9512  }
9513  else if(Ptr->Command == "Fer")
9514  {
9515  AnsiString AllowedExits;
9516  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
9517  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
9518  }
9519  else if(Ptr->Command == "Fjo")
9520  {
9521  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
9522  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9523  }
9524  else if(Ptr->Command == "jbo")
9525  {
9526  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
9527  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9528  }
9529  else if(Ptr->Command == "fsp")
9530  {
9531  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
9532  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9533  }
9534  else if(Ptr->Command == "rsp")
9535  {
9536  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
9537  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9538  }
9539  else if(Ptr->Command == "cdt")
9540  {
9541  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
9542  }
9543  if(RetStr != "")
9544  {
9545  RetStr = RetStr + '\n' + PartStr;
9546  }
9547  else
9548  {
9549  RetStr = PartStr;
9550  }
9551  Count++;
9552  }
9553  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
9554  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
9555  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
9556  // forward as anyone should wish to see without looking at the full timetable
9557  Utilities->CallLogPop(2072);
9558  return(RetStr);
9559 }
9560 
9561 // ---------------------------------------------------------------------------
9562 
9563 AnsiString TTrainController::ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
9564 {
9565  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
9566  + AnsiString(RptNum) + ",ControllerCheckNewServiceDepartureTime," + TDEPtr->HeadCode);
9567  AnsiString DepTime = "", EventTime = "";
9568  bool CDTFlag = false; //reports if train changes direction before departs
9569  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
9570  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
9571  {
9572  if(AVI->Command == "cdt")
9573  {
9574  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
9575  continue;
9576  }
9577  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
9578  {
9579  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
9580  RetStr += "\nNew service splits at " + EventTime;
9581  Utilities->CallLogPop(2237);
9582  return(RetStr);
9583  }
9584  if(AVI->Command == "jbo")
9585  {
9586  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
9587  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
9588  Utilities->CallLogPop(2238);
9589  return(RetStr);
9590  }
9591  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
9592  {
9593  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
9594  if(CDTFlag)
9595  {
9596  RetStr += "\nNew service changes direction then departs at " + DepTime;
9597  }
9598  else
9599  {
9600  RetStr += "\nNew service departs at " + DepTime;
9601  }
9602  Utilities->CallLogPop(2239);
9603  return(RetStr);
9604  }
9605  }
9606  Utilities->CallLogPop(2223);
9607  return(RetStr);
9608 }
9609 
9610 // ---------------------------------------------------------------------------
9611 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
9612 /*
9613  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
9614  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
9615  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
9616 
9617  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
9618  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
9619  user wishes
9620 
9621  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9622  descriptive text or anything user wishes
9623  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9624  be ignored) is taken as the timetable start time.
9625  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9626  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9627  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9628  within the timetable if required.
9629  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9630  services)
9631  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9632  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9633 
9634  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9635  text timetable file easier
9636 
9637  form:-
9638  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9639  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9640  then multiple entries, separated by commas, of the form:-
9641 
9642  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9643  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
9644  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
9645 
9646  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
9647  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
9648  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
9649 
9650  HH:MM;Command (cdt) }TimeCmd }
9651  HH:MM;Location (arr & dep) }TimeLoc }
9652  HH:MM;HH:MM;Location }TimeTimeLoc }
9653  HH:MM;pas;Location }PassTime }
9654  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
9655  HH:MM;Fer;set of allowable IDs }ExitRailway }
9656  Command (Frh only) }FinRemHere }
9657 
9658  R;mm;dd;nn. Repeat Repeat entry
9659 
9660  Formats:
9661 
9662  Command only: Frh
9663  Time;Command: cdt
9664  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
9665  Time;Command;2 Element IDs: Snt
9666  Time;Comand;n Element IDs: Fer
9667  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
9668  Time;Command;2 Element IDs;Headcode Snt-sh
9669  Time;Command;Location pas
9670  Time;Location Arr Dep
9671  Time;Time;Location Arr & dep together
9672 
9673  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
9674 
9675  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
9676  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
9677  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
9678 
9679  4 2x Linked entries, all shuttles:
9680 
9681  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
9682  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
9683  -> Remain Here (at finish location after all repeats)
9684  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
9685  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
9686 
9687  Allowable successors:-
9688 
9689  Successor state Type
9690 
9691  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
9692  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
9693  Sfs AtLoc )
9694  Sns AtLoc ) Start
9695  Sns-fsh AtLoc )
9696  Snt-sh AtLoc )
9697  Sns-sh AtLoc )
9698 
9699  pas Moving )
9700  jbo AtLoc )
9701  fsp AtLoc )
9702  rsp AtLoc ) Intermediate
9703  cdt AtLoc )
9704  TimeLoc arr Moving (bef) )
9705  TimeLoc dep AtLoc (bef) )
9706  TimeTimeLoc Moving )
9707 
9708  Fns Repeat/Nothing)
9709  Fjo Repeat/Nothing)
9710  Frh Repeat/Nothing)
9711  Fer Repeat/Nothing) Finish
9712  Frh-sh Repeat )
9713  Fns-sh Repeat )
9714  F-nshs Nothing )
9715 
9716  Descriptions:
9717  Snt New train
9718  Sfs New service from split
9719  Sns New service from another service
9720  Sns-fsh New non-repeating service from a shuttle service
9721  Snt-sh New shuttle train at a timetabled stop
9722  Sns-sh New shuttle service from a feeder service
9723 
9724  pas Pass
9725  jbo Be joined by another train
9726  fsp Front split
9727  rsp Rear split
9728  cdt Change direction of train
9729  TimeLoc arr Arrival
9730  TimeLoc dep Departure
9731  TimeTimeLoc Arrival and departure
9732 
9733  Fns Finish & form a new service
9734  Fjo Finish & join another train
9735  Frh Finish & remain here
9736  Fer Finish & exit railway
9737  Frh-sh Finish & repeat shuttle, finally remain here
9738  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
9739  F-nshs Finish & form a shuttle feeder service
9740 */
9741 
9742 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
9743 {
9744  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
9745  // a line that is too long; timetable containing too few lines; and timetable failed to open.
9746  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
9747  // new for v0.2b
9748  // compile ActiveTrackElementNameMap
9749  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
9750 
9752  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
9753  {
9754  // if((Track->TrackVector.at(x).ActiveTrackElementName != "") && (Track->TrackVector.at(x).TrackType != Continuation))
9755  if((Track->TrackVector.at(x).ActiveTrackElementName != "") && (Track->ContinuationNameMap.find(Track->TrackVector.at(x).ActiveTrackElementName))
9756  == Track->ContinuationNameMap.end())
9757  {
9758  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
9759  ActiveTrackElementNameMapEntry.first = Track->TrackVector.at(x).ActiveTrackElementName;
9760  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
9761  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
9762  }
9763  }
9765  // end of new section
9766  std::ifstream TTBLFile(FileName, std::ios_base::binary);
9767 
9768  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
9769  if(TTBLFile.is_open())
9770  {
9771  char *TrainTimetableString = new char[10000];
9772  // enough for over 200 stations, should be adequate!
9773  bool EndOfFile = false;
9774  int Count = 0;
9775  // counts 'relevant' lines, i.e ignores any before the start time on its own line
9776  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9777  // delimiter is '\0' as it's an AnsiString
9778  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9779  // file empty - stores a null in 1st position if doesn't load any characters
9780  {
9781  // may still have eof even if read a line (no CRLF at end), and
9782  // if so need to process it
9783  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
9784  TTBLFile.close();
9785  delete[] TrainTimetableString;
9786  Utilities->CallLogPop(1611);
9787  return(false);
9788  }
9789  AnsiString OneLine(TrainTimetableString);
9790  bool FinalCallFalse = false;
9791  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9792  // get rid of lines before the start time
9793  {
9794  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
9795  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9796  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9797  // stores a null in 1st position if doesn't load any characters
9798  {
9799  // may still have eof even if read a line (no CRLF at end), and
9800  // if so need to process it
9801  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
9802  TTBLFile.close();
9803  delete[] TrainTimetableString;
9804  Utilities->CallLogPop(772);
9805  return(false);
9806  }
9807  OneLine = AnsiString(TrainTimetableString);
9808  }
9809  // here when have accepted the start time
9810  Count++; // increment past the start time
9811  while(!EndOfFile)
9812  {
9813  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9814  // get next line after start time
9815  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9816  // stores a null in 1st position if doesn't load any characters
9817  {
9818  // may still have eof even if read a line (no CRLF at end), and
9819  // if so need to process it
9820  EndOfFile = true;
9821  OneLine = "";
9822  }
9823  else
9824  {
9825  OneLine = AnsiString(TrainTimetableString);
9826  }
9827  if(OneLine.Length() > 9999)
9828  {
9829  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
9830  TTBLFile.close();
9831  delete[] TrainTimetableString;
9832  Utilities->CallLogPop(789);
9833  return(false);
9834  }
9835  bool FinalCallFalse = false;
9836  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9837  // false for FinalCall - just checking at this stage
9838  {
9839  TTBLFile.close();
9840  delete[] TrainTimetableString;
9841  Utilities->CallLogPop(770);
9842  return(false);
9843  }
9844  if(EndOfFile && (Count < 2))
9845  // Timetable must contain at least two relevant lines, one for start time and at least one train
9846  {
9847  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
9848  TTBLFile.close();
9849  delete[] TrainTimetableString;
9850  Utilities->CallLogPop(771);
9851  return(false);
9852  }
9853  Count++;
9854  }
9855  delete[] TrainTimetableString;
9856  TTBLFile.close();
9857  } // if(TTBLFile.is_open())
9858  else
9859  {
9860  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's not open in another application");
9861  Utilities->CallLogPop(2154);
9862  return(false);
9863  }
9864  Utilities->CallLogPop(753);
9865  return(true);
9866 }
9867 
9868 // ---------------------------------------------------------------------------
9869 
9870 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
9871  bool CheckLocationsExistInRailway) // return true for success
9872 
9873 /* Format:
9874  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9875  descriptive text or anything user wishes
9876  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9877  be ignored) is taken as the timetable start time.
9878  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9879  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9880  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9881  within the timetable if required.
9882  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9883  services)
9884  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9885  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9886 
9887  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9888  text timetable file easier
9889 
9890  form:-
9891  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9892  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9893  then multiple entries, separated by commas, of the form:-
9894 
9895  Format FormatType
9896  [W]HH:MM;Command (cdt) }TimeCmd }
9897  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
9898  [W]HH:MM;pas;Location }PassTime }
9899  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9900  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
9901  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
9902  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
9903  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
9904  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
9905  [W]HH:MM;Fns-sh;Details }FSHNewService }
9906  [W]HH:MM;Location (arr & dep) }TimeLoc }
9907  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
9908  Command (Frh only) }FinRemHere }
9909 
9910  R;mm;dd;nn. Repeat Repeat entry
9911 
9912  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
9913  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
9914  at location; or (c) departure time if train already at location (including train started at location either as a new
9915  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
9916  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
9917  minutes, incremental train headcode last 2 digits, and number of repeats.
9918 
9919  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
9920  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
9921  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
9922  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
9923  (it's for a shuttle train to return to depot at end of services)
9924 
9925  Command/Location & details are as follows:-
9926 
9927  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
9928  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
9929  2E44 in its Sfs entry. All these are checked.
9930  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
9931 
9932  Start commands:-
9933  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
9934  with loc as a start entry can't have a location as details)
9935  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
9936  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
9937  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
9938  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
9939  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
9940  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
9941  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
9942  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
9943  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
9944  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
9945  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
9946 
9947  Intermediate commands:-
9948  Time - Location (TimeLoc), can be arrival or departure depending on context
9949  Time Time location (TimeTimeLoc), arrival and departure
9950  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
9951  pas (PassTime), Time;pas;Location
9952  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
9953  joining train's finish details must correspond or the file check will fail
9954  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
9955  new train - that train's starting information must correspond)
9956  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
9957  new train - that train's starting information must correspond)
9958  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
9959 
9960  Finish commands:-
9961  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
9962  creation)
9963  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
9964  shuttle headcode (no train creation)
9965  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
9966  may have to wait for it), details = new headcode (delete train)
9967  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
9968  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
9969  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
9970  here
9971  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
9972  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
9973 
9974  Repeat:-
9975  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
9976  headcodes - it is up to user to avoid duplicates if he/she wishes to.
9977 
9978  Checks carried out with error messages in this function:-
9979  At least one comma in a service line (it's based on a .csv file)
9980  No entries following train information;
9981  At least one comma in remainder after train information (i.e at least a start and a finish entry);
9982  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
9983  First entry not a start entry;
9984  Train information incomplete before a start entry;
9985  Entry follows a finish entry but doesn't begin with 'R';
9986  SplitEntry returns false in a finish entry - message repeats the entry for information;
9987  Last action entry isn't a finish entry.
9988 
9989  Function returns false with no message if:-
9990  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
9991  time is found at all then an error message is given in the calling function);
9992  SplitTrainInfo returns false (message given in called function);
9993  SplitRepeat returns false (message given in called function).
9994 */{
9995  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
9996  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
9997  TTrainDataEntry TempTrainDataEntry;
9998 
9999  EndOfFile = false;
10000  StripSpaces(0, OneLine);
10001  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
10002  // semicolons within the line
10003  ServiceReference = "";
10004  if(OneLine != "")
10005  {
10006  if(OneLine[1] != '*')
10007  {
10008  int SCPos = OneLine.Pos(';');
10009  if(SCPos == 0)
10010  {
10011  ServiceReference = OneLine.SubString(1, 8);
10012  }
10013  else
10014  {
10015  ServiceReference = OneLine.SubString(1, (SCPos - 1));
10016  }
10017  }
10018  }
10019  bool AllCommas = true;
10020 
10021  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
10022  {
10023  if(OneLine[x] != ',')
10024  {
10025  AllCommas = false;
10026  }
10027  }
10028  if(AllCommas || (OneLine == ""))
10029  {
10030  if(Count > 0)
10031  {
10032  EndOfFile = true;
10033  // returns true for a blank line - treated as end of file
10034  Utilities->CallLogPop(1018);
10035  return(true);
10036  }
10037  else // count == 0 so not yet found a start time, no message to be given
10038  {
10039  Utilities->CallLogPop(754);
10040  return(false);
10041  }
10042  }
10043  AnsiString First = "", Second = "", Third = "", Fourth = "";
10044  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
10045  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
10046  TDateTime StartTime(0);
10047  TExitList ExitList;
10048  bool Warning = false;
10049 
10050  if(Count == 0) // no start time found yet
10051  {
10052 /* dropped at v0.6b
10053  AnyHeadCodeValid = false;
10054  if(OneLine.SubString(6,5) == ";0000")
10055  {
10056  AnyHeadCodeValid = true;
10057  }
10058 */
10059  if(!CheckTimeValidity(0, OneLine, StartTime))
10060  {
10061  // no message is given for an invalid time as it's assumed to be an irrelevant line
10062  // if no start time is found at all then an error message is given in the calling function
10063  // AnyHeadCodeValid = false;
10064  Utilities->CallLogPop(755);
10065  return(false);
10066  }
10067  if(FinalCall) // here if start time valid
10068  {
10069  TTClockTime = StartTime;
10070  TimetableStartTime = StartTime;
10071  }
10072  }
10073  else
10074  {
10075  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
10076  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
10077  double MaxBrakeRate = 0;
10078  double PowerAtRail = 0;
10079  int SignallerSpeed = 0;
10080  if(OneLine[1] == '*')
10081  {
10082  Utilities->CallLogPop(1581);
10083  return(true);
10084  // ignore any line beginning with '*' but return true as there is no error
10085  }
10086  int Pos = OneLine.Pos(',');
10087  if(Pos == 0)
10088  {
10089  int SubStringLength = 20;
10090  if(OneLine.Length() < 20)
10091  {
10092  SubStringLength = OneLine.Length();
10093  }
10094  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
10095  Utilities->CallLogPop(766);
10096  return(false);
10097  }
10098  TrainInfoStr = OneLine.SubString(1, Pos - 1);
10099  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
10100  GiveMessages)) // error messages given in SplitTrainInfo
10101  {
10102  Utilities->CallLogPop(773);
10103  return(false);
10104  }
10105  if(FinalCall)
10106  {
10107  // store Train info - conversions done in SplitTrainInfo
10108  // only headcode mandatory for continuing services
10109  TempTrainDataEntry.HeadCode = HeadCode;
10110  TempTrainDataEntry.ServiceReference = HeadCode;
10111  TempTrainDataEntry.Description = Description;
10112  TempTrainDataEntry.StartSpeed = StartSpeed;
10113  TempTrainDataEntry.Mass = Mass;
10114  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
10115  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
10116  TempTrainDataEntry.PowerAtRail = PowerAtRail;
10117  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
10118  TTrainOperatingData TempTrainOperatingData;
10119  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
10120  }
10121  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
10122  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
10123  // so strip them off
10124  while(NewRemainder[NewRemainder.Length()] == ',')
10125  {
10126  if(NewRemainder.Length() > 1)
10127  {
10128  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
10129  }
10130  else
10131  {
10132  NewRemainder = "";
10133  break;
10134  }
10135  }
10136  // check if zero length & fail if so
10137  if(NewRemainder == "")
10138  {
10139  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
10140  Utilities->CallLogPop(769);
10141  return(false);
10142  }
10143  // now have one more entry than there are commas
10144  int CommaCount = 0;
10145  for(int x = 1; x < NewRemainder.Length() + 1; x++)
10146  {
10147  if(NewRemainder[x] == ',')
10148  {
10149  CommaCount++;
10150  }
10151  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
10152  if(CommaCount == 0)
10153  {
10154  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
10155  {
10156  int SubStringLength = 20;
10157  if(OneLine.Length() < 20)
10158  {
10159  SubStringLength = OneLine.Length();
10160  }
10161  TimetableMessage(GiveMessages,
10162  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
10163  OneLine.SubString(1, SubStringLength) + "'....");
10164  Utilities->CallLogPop(783);
10165  return(false);
10166  }
10167  }
10168  AnsiString OneEntry = "";
10169  TTimetableFormatType FormatType;
10170  TTimetableSequenceType SequenceType;
10171  TTimetableLocationType LocationType;
10172  TTimetableShuttleLinkType ShuttleLinkType;
10173  bool FinishFlag = false;
10174  for(int x = 0; x < CommaCount + 1; x++)
10175  {
10176  if((CommaCount == 0) || (x < CommaCount))
10177  // i.e. train entered under signaller control with no repeats, or entry is not the last,
10178  // in which case there's a comma & finish element or repeat still to come this entry could
10179  // be a finish but can't be a repeat
10180  {
10181  if(CommaCount == 0)
10182  {
10183  OneEntry = NewRemainder;
10184  NewRemainder = "";
10185  }
10186  else
10187  {
10188  Pos = NewRemainder.Pos(',');
10189  OneEntry = NewRemainder.SubString(1, Pos - 1);
10190  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
10191  }
10192  First = "";
10193  Second = "";
10194  Third = "";
10195  Fourth = "";
10196  RearStartOrRepeatMins = 0;
10197  FrontStartOrRepeatDigits = 0;
10198  NumberOfRepeats = 0;
10199  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10200  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10201  {
10202  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10203  Utilities->CallLogPop(756);
10204  return(false);
10205  }
10206  // check if warning for Frh or Fjo & reject
10207  if(Warning && (Second == "Frh"))
10208  {
10209  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
10210  Utilities->CallLogPop(1793);
10211  return(false);
10212  }
10213  if(Warning && (Second == "Fjo"))
10214  {
10215  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
10216  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
10217  Utilities->CallLogPop(1794);
10218  return(false);
10219  }
10220  if(x == 0) // should be start event
10221  {
10222  if(SequenceType != Start)
10223  {
10224  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
10225  Utilities->CallLogPop(784);
10226  return(false);
10227  }
10228  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
10229  {
10230  if(NewRemainder[1] != 'R')
10231  {
10232  TimetableMessage(GiveMessages,
10233  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
10234  OneEntry + "'");
10235  Utilities->CallLogPop(787);
10236  return(false);
10237  }
10238  }
10239  if((Second == "Snt") || (Second == "Snt-sh"))
10240  // need full train information including non-default values for at least HeadCode, Description,
10241  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
10242  {
10243  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
10244  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
10245  {
10246  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
10247  OneEntry + "'");
10248  Utilities->CallLogPop(1783);
10249  return(false);
10250  }
10251  }
10252  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
10253  // service continuation - need at least non-default value for HeadCode
10254  {
10255  if(HeadCode == "")
10256  {
10257  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10258  OneEntry + "'");
10259  Utilities->CallLogPop(788);
10260  return(false);
10261  }
10262  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
10263  {
10264  TimetableMessage(GiveMessages,
10265  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10266  OneEntry + "'");
10267  Utilities->CallLogPop(843);
10268  return(false);
10269  }
10270  }
10271  }
10272  if(SequenceType == Finish)
10273  {
10274  FinishFlag = true;
10275  // marker for only permitted additional entry being a repeat, only needed if the
10276  // finish entry is not the last entry
10277  }
10278  if(FinalCall)
10279  {
10280  // interpret & add to ActionVector
10281  TDateTime TempTime;
10282  TActionVectorEntry ActionVectorEntry;
10283  ActionVectorEntry.FormatType = FormatType;
10284  ActionVectorEntry.LocationType = LocationType;
10285  ActionVectorEntry.SequenceType = SequenceType;
10286  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10287  ActionVectorEntry.Warning = Warning;
10288  if(FormatType == TimeLoc)
10289  {
10290  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
10291  {
10292  ;
10293  } // these will all be true as final call
10294 
10295  ActionVectorEntry.LocationName = Second;
10296  }
10297  else if(FormatType == PassTime) // new
10298  {
10299  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
10300  {
10301  ;
10302  }
10303  ActionVectorEntry.Command = Second;
10304  ActionVectorEntry.LocationName = Third;
10305  }
10306  else if(FormatType == TimeTimeLoc)
10307  {
10308  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
10309  {
10310  ;
10311  }
10312  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
10313  {
10314  ;
10315  }
10316  ActionVectorEntry.LocationName = Third;
10317  }
10318  else if(FormatType == TimeCmd)
10319  {
10320  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
10321  {
10322  ;
10323  }
10324  ActionVectorEntry.Command = Second;
10325  }
10326  else if(FormatType == ExitRailway)
10327  {
10328  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
10329  {
10330  ;
10331  }
10332  ActionVectorEntry.Command = Second;
10333  ActionVectorEntry.ExitList = ExitList;
10334  }
10335  else if(FormatType == StartNew)
10336  {
10337  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
10338  {
10339  ;
10340  }
10341  ActionVectorEntry.Command = Second;
10342  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10343  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10344  if(Fourth == 'S')
10345  {
10346  ActionVectorEntry.SignallerControl = true;
10347  }
10348  }
10349  else if(FormatType == SNTShuttle)
10350  {
10351  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
10352  {
10353  ;
10354  }
10355  ActionVectorEntry.Command = Second;
10356  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10357  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10358  ActionVectorEntry.OtherHeadCode = Fourth;
10359  }
10360  else if(FormatType == SNSShuttle)
10361  {
10362  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
10363  {
10364  ;
10365  }
10366  ActionVectorEntry.Command = Second;
10367  ActionVectorEntry.OtherHeadCode = Third;
10368  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10369  }
10370  else if(FormatType == TimeCmdHeadCode)
10371  {
10372  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
10373  {
10374  ;
10375  }
10376  ActionVectorEntry.Command = Second;
10377  ActionVectorEntry.OtherHeadCode = Third;
10378  }
10379  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
10380  {
10381  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
10382  {
10383  ;
10384  }
10385  ActionVectorEntry.Command = Second;
10386  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10387  }
10388  else if(FormatType == FSHNewService)
10389  {
10390  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
10391  {
10392  ;
10393  }
10394  ActionVectorEntry.Command = Second;
10395  ActionVectorEntry.OtherHeadCode = Third;
10396  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10397  }
10398  else if(FormatType == FinRemHere)
10399  {
10400  ActionVectorEntry.Command = Second;
10401  }
10402  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10403  }
10404  }
10405  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
10406  {
10407  OneEntry = NewRemainder;
10408  First = "";
10409  Second = "";
10410  Third = "";
10411  Fourth = "";
10412  RearStartOrRepeatMins = 0;
10413  FrontStartOrRepeatDigits = 0;
10414  NumberOfRepeats = 0;
10415  if((FinishFlag) && (OneEntry[1] != 'R'))
10416  // already had a finish entry
10417  {
10418  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
10419  Utilities->CallLogPop(79);
10420  return(false);
10421  }
10422  if(OneEntry[1] != 'R') // must be finish
10423  {
10424  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10425  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10426  {
10427  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10428  Utilities->CallLogPop(757);
10429  return(false);
10430  }
10431  if(SequenceType != Finish)
10432  {
10433  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
10434  Utilities->CallLogPop(785);
10435  return(false);
10436  }
10437  if(FinalCall)
10438  {
10439  // interpret & add to ActionVector
10440  TDateTime TempTime;
10441  TActionVectorEntry ActionVectorEntry;
10442  ActionVectorEntry.FormatType = FormatType;
10443  ActionVectorEntry.LocationType = LocationType;
10444  ActionVectorEntry.SequenceType = SequenceType;
10445  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10446  ActionVectorEntry.Warning = Warning;
10447  if(FormatType == TimeCmd)
10448  {
10449  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
10450  {
10451  ;
10452  }
10453  ActionVectorEntry.Command = Second;
10454  }
10455  else if(FormatType == TimeCmdHeadCode)
10456  {
10457  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
10458  {
10459  ;
10460  }
10461  ActionVectorEntry.Command = Second;
10462  ActionVectorEntry.OtherHeadCode = Third;
10463  }
10464  else if(FormatType == FNSNonRepeatToShuttle)
10465  {
10466  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
10467  {
10468  ;
10469  }
10470  ActionVectorEntry.Command = Second;
10471  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10472  }
10473  else if(FormatType == FSHNewService)
10474  {
10475  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
10476  {
10477  ;
10478  }
10479  ActionVectorEntry.Command = Second;
10480  ActionVectorEntry.OtherHeadCode = Third;
10481  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10482  }
10483  else if(FormatType == ExitRailway)
10484  {
10485  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
10486  {
10487  ;
10488  }
10489  ActionVectorEntry.Command = Second;
10490  ActionVectorEntry.ExitList = ExitList;
10491  }
10492  else if(FormatType == FinRemHere)
10493  {
10494  ActionVectorEntry.Command = Second;
10495  }
10496  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10497  }
10498  }
10499  else // repeat
10500  {
10501  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
10502  {
10503  Utilities->CallLogPop(786);
10504  // error messages given in SplitRepeat
10505  return(false);
10506  }
10507  if(FinalCall)
10508  {
10509  TActionVectorEntry ActionVectorEntry;
10510  ActionVectorEntry.FormatType = Repeat;
10511  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
10512  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
10513  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
10514  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10515  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10516  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
10517  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10518  }
10519  }
10520  }
10521  }
10522  if(FinalCall)
10523  {
10524  TrainDataVector.push_back(TempTrainDataEntry);
10525  }
10526  }
10527  Utilities->CallLogPop(80);
10528  return(true);
10529 }
10530 
10531 // ---------------------------------------------------------------------------
10532 
10533 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
10534 {
10535  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
10536  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
10537  {
10538  Utilities->CallLogPop(1890);
10539  return(false);
10540  }
10541  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
10542  {
10543  Utilities->CallLogPop(1891);
10544  return(false);
10545  }
10546  Utilities->CallLogPop(1892);
10547  return(true);
10548 }
10549 
10550 // ---------------------------------------------------------------------------
10551 
10552 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
10553 // 1st 5 chars must be HH:MM, anything else will be ignored
10554 {
10555  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
10556  if(TimeStr.Length() < 5)
10557  {
10558  Utilities->CallLogPop(926);
10559  return(false);
10560  }
10561  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
10562  {
10563  Utilities->CallLogPop(927);
10564  return(false);
10565  }
10566  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
10567  {
10568  Utilities->CallLogPop(928);
10569  return(false);
10570  }
10571  if(TimeStr[3] != ':')
10572  {
10573  Utilities->CallLogPop(929);
10574  return(false);
10575  }
10576  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
10577  {
10578  Utilities->CallLogPop(930);
10579  return(false);
10580  }
10581  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
10582  {
10583  Utilities->CallLogPop(931);
10584  return(false);
10585  }
10586  while(TimeStr.Length() > 5)
10587  {
10588  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
10589  }
10590  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
10591  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
10592 
10593  if((WholeHours + FracHour) >= 95.98334)
10594  {
10595  Utilities->CallLogPop(1817);
10596  return(false); // > 95h 59m
10597  }
10598  Time = TDateTime((WholeHours + FracHour) / 24);
10599  Utilities->CallLogPop(932);
10600  return(true);
10601 }
10602 
10603 // ---------------------------------------------------------------------------
10604 
10605 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
10606  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
10607  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
10608 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
10609  Return false for failure.
10610  See description above under ProcessOneTimetableLinefor details of train action entries
10611  NB all types set except LocationType for Sns as may be located or not
10612 */{
10613  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
10614  Warning = false;
10615  TDateTime TempTime;
10616 
10617  if(OneEntry.Length() > 0)
10618  {
10619  if(OneEntry[1] == 'W') // warning
10620  {
10621  Warning = true;
10622  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
10623  // strip it off
10624  }
10625  }
10626  if(OneEntry == "Frh")
10627  {
10628  FormatType = FinRemHere;
10629  SequenceType = Finish;
10630  LocationType = AtLocation;
10631  ShuttleLinkType = NotAShuttleLink;
10632  Second = "Frh";
10633  Utilities->CallLogPop(1016);
10634  return(true);
10635  }
10636  if(OneEntry.Length() < 7)
10637  {
10638  Utilities->CallLogPop(907);
10639  return(false); // 'HH:MM;' + at least a one-letter location name
10640  }
10641  int Pos = OneEntry.Pos(';'); // first segment delimiter
10642 
10643  if(Pos != 6)
10644  {
10645  Utilities->CallLogPop(908);
10646  return(false);
10647  // no delimiter or delimiter not in position 6, has to be a time so fail
10648  }
10649  First = OneEntry.SubString(1, 5); // has to be a time
10650  if(!CheckTimeValidity(16, First, TempTime))
10651  {
10652  Utilities->CallLogPop(909);
10653  return(false);
10654  }
10655  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
10656 
10657  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
10658  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
10659  {
10660  if(Remainder.Length() < 7)
10661  {
10662  Utilities->CallLogPop(910);
10663  return(false); // 'HH:MM;' + at least a one-letter location name
10664  }
10665  Pos = Remainder.Pos(';'); // second segment delimiter
10666  if(Pos == 0)
10667  {
10668  Utilities->CallLogPop(911);
10669  return(false);
10670  // no delimiter, has to be one between departure time & location
10671  }
10672  Second = Remainder.SubString(1, 5); // has to be a time
10673  if(!CheckTimeValidity(15, Second, TempTime))
10674  {
10675  Utilities->CallLogPop(912);
10676  return(false);
10677  }
10678  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10679  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
10680  {
10681  Utilities->CallLogPop(913);
10682  return(false);
10683  }
10684  FormatType = TimeTimeLoc;
10685  SequenceType = Intermediate;
10686  LocationType = AtLocation;
10687  ShuttleLinkType = NotAShuttleLink;
10688  Utilities->CallLogPop(914);
10689  return(true);
10690  }
10691  Pos = Remainder.Pos(';'); // second segment delimiter
10692  if(Pos == 0) // no third segment so second must be a location, or cdt
10693  {
10694  Second = Remainder;
10695  if(Second == "cdt")
10696  {
10697  FormatType = TimeCmd;
10698  ShuttleLinkType = NotAShuttleLink;
10699  LocationType = AtLocation;
10700  SequenceType = Intermediate;
10701  Utilities->CallLogPop(915);
10702  return(true);
10703  }
10704  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
10705  {
10706  Utilities->CallLogPop(916);
10707  return(false);
10708  }
10709  else
10710  {
10711  FormatType = TimeLoc;
10712  LocationType = AtLocation;
10713  SequenceType = Intermediate;
10714  ShuttleLinkType = NotAShuttleLink;
10715  Utilities->CallLogPop(917);
10716  return(true);
10717  }
10718  }
10719  // here if second segment is a command, with a third & maybe fourth segments as details
10720  if((Pos != 4) && (Pos != 7) && (Pos != 8))
10721  {
10722  Utilities->CallLogPop(918);
10723  return(false);
10724  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
10725  }
10726  Second = Remainder.SubString(1, Pos - 1); // command
10727 
10728  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10729  // details
10730  Pos = Remainder.Pos(';'); // third segment delimiter
10731  if(Pos == 0)
10732  {
10733  Third = Remainder;
10734  }
10735  else
10736  {
10737  Third = Remainder.SubString(1, Pos - 1);
10738  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10739  }
10740  if((Second == "Snt") || (Second == "Snt-sh"))
10741  // third has to be 2 element idents with a space between
10742  {
10743  int SpacePos = Third.Pos(' ');
10744  if(SpacePos == 0)
10745  {
10746  Utilities->CallLogPop(919);
10747  return(false); // no space
10748  }
10749  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
10750  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
10751  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
10752  if(CheckLocationsExistInRailway)
10753  {
10754  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
10755  {
10756  Utilities->CallLogPop(920);
10757  return(false);
10758  }
10759  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
10760  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
10761  }
10762  if(Second == "Snt")
10763  {
10764  FormatType = StartNew;
10765  SequenceType = Start;
10766  LocationType = NoLocation;
10767  // can't be set until know whether located or not - done in SecondPassActions
10768  ShuttleLinkType = NotAShuttleLink;
10769  }
10770  else // Snt-sh
10771  {
10772  FormatType = SNTShuttle;
10773  LocationType = AtLocation;
10774  SequenceType = Start;
10775  ShuttleLinkType = ShuttleLink;
10776  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
10777  {
10778  Utilities->CallLogPop(1038);
10779  return(false);
10780  }
10781  }
10782  Utilities->CallLogPop(921);
10783  return(true);
10784  }
10785  if(Second == "Sns-sh") // third & fourth have to be headcodes
10786  {
10787  FormatType = SNSShuttle;
10788  LocationType = AtLocation;
10789  SequenceType = Start;
10790  ShuttleLinkType = ShuttleLink;
10791  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
10792  {
10793  Utilities->CallLogPop(1039);
10794  return(false);
10795  }
10796  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
10797  {
10798  Utilities->CallLogPop(1040);
10799  return(false);
10800  }
10801  Utilities->CallLogPop(1041);
10802  return(true);
10803  }
10804  if(Second == "F-nshs")
10805  {
10806  FormatType = FNSNonRepeatToShuttle;
10807  LocationType = AtLocation;
10808  SequenceType = Finish;
10809  ShuttleLinkType = ShuttleLink;
10810  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
10811  {
10812  Utilities->CallLogPop(1047);
10813  return(false);
10814  }
10815  Utilities->CallLogPop(1048);
10816  return(true);
10817  }
10818  if(Second == "Sns-fsh")
10819  {
10820  FormatType = SNSNonRepeatFromShuttle;
10821  LocationType = AtLocation;
10822  SequenceType = Start;
10823  ShuttleLinkType = ShuttleLink;
10824  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
10825  {
10826  Utilities->CallLogPop(1098);
10827  return(false);
10828  }
10829  Utilities->CallLogPop(1099);
10830  return(true);
10831  }
10832  if(Second == "Fns-sh") // third & fourth have to be headcodes
10833  {
10834  FormatType = FSHNewService;
10835  LocationType = AtLocation;
10836  SequenceType = Finish;
10837  ShuttleLinkType = ShuttleLink;
10838  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
10839  {
10840  Utilities->CallLogPop(1050);
10841  return(false);
10842  }
10843  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
10844  {
10845  Utilities->CallLogPop(1051);
10846  return(false);
10847  }
10848  Utilities->CallLogPop(1052);
10849  return(true);
10850  }
10851  // new segment for 'pas'
10852  if(Second == "pas") // third has to be a location
10853  {
10854  FormatType = PassTime;
10855  LocationType = EnRoute;
10856  SequenceType = Intermediate;
10857  ShuttleLinkType = NotAShuttleLink;
10858  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
10859  {
10860  Utilities->CallLogPop(1515);
10861  return(false);
10862  }
10863  Utilities->CallLogPop(1516);
10864  return(true);
10865  }
10866  // new segment for revised 'Fer'
10867  if(Second == "Fer")
10868  // third has to be a set of IDs separated by spaces, and at least 1
10869  {
10870  FormatType = ExitRailway;
10871  LocationType = EnRoute;
10872  SequenceType = Finish;
10873  ShuttleLinkType = NotAShuttleLink;
10874  if(CheckLocationsExistInRailway)
10875  {
10876  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
10877  {
10878  Utilities->CallLogPop(1519);
10879  return(false);
10880  }
10881  }
10882  Utilities->CallLogPop(1520);
10883  return(true);
10884  }
10885  // all remainder must be TimeCmdHeadCode types to be valid
10886  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
10887  (Second != "Frh-sh"))
10888  {
10889  Utilities->CallLogPop(922);
10890  return(false); // all TimeCmdHeadCode types
10891  }
10892  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
10893  {
10894  Utilities->CallLogPop(923);
10895  return(false);
10896  }
10897  FormatType = TimeCmdHeadCode;
10898  LocationType = AtLocation;
10899  if(Second == "Frh-sh")
10900  {
10901  ShuttleLinkType = ShuttleLink;
10902  }
10903  else
10904  {
10905  ShuttleLinkType = NotAShuttleLink;
10906  }
10907  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
10908  {
10909  SequenceType = Finish;
10910  }
10911  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
10912  {
10913  SequenceType = Intermediate;
10914  }
10915  if((Second == "Sfs") || (Second == "Sns"))
10916  {
10917  SequenceType = Start;
10918  }
10919  Utilities->CallLogPop(924);
10920  return(true);
10921 }
10922 
10923 // ---------------------------------------------------------------------------
10924 
10925 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
10926 {
10927  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
10928  // and contains no special characters
10929  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
10930  if(LocStr == "")
10931  {
10932  Utilities->CallLogPop(1353);
10933  return(false); // has to have at least one character
10934  }
10935  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
10936  {
10937  Utilities->CallLogPop(1354);
10938  return(false); // can't begin with a number
10939  }
10940  for(int x = 1; x < LocStr.Length() + 1; x++)
10941  {
10942  if(LocStr[x] < ' ')
10943  {
10944  Utilities->CallLogPop(1355);
10945  return(false); // contains a special character
10946  }
10947  if(LocStr[x] > 'z')
10948  {
10949  Utilities->CallLogPop(1356);
10950  return(false); // contains a character outside the standard ASCII set
10951  }
10952  }
10953  // check exists in railway location list if CheckLocationsExistInRailway is true
10954  if(CheckLocationsExistInRailway)
10955  {
10956  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
10957  {
10958  TimetableMessage(GiveMessages, "Location name '" + LocStr +
10959  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
10960  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
10961  "that includes a continuation will not be valid.");
10962  Utilities->CallLogPop(1357);
10963  return(false);
10964  }
10965  }
10966  Utilities->CallLogPop(1358);
10967  return(true);
10968 }
10969 
10970 // ---------------------------------------------------------------------------
10971 
10972 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
10973 {
10974  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
10975  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
10976  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
10977  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
10978  HeadCode);
10979  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
10980  {
10981  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
10982  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Snt-sh, Sns-sh, Fns-sh or Frh-sh");
10983  Utilities->CallLogPop(1359);
10984  return(false);
10985  }
10986  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
10987  for(int x = 1; x < (HeadCode.Length() + 1); x++)
10988  {
10989  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
10990  {
10991  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
10992  Utilities->CallLogPop(1895);
10993  return(false);
10994  }
10995  }
10996  // secondly ensure the true Headcode only has letters or digits
10997  for(int x = 3; x >= 0; x--)
10998  {
10999  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
11000  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
11001  {
11002  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
11003  Utilities->CallLogPop(1790);
11004  return(false);
11005  }
11006  }
11007  Utilities->CallLogPop(1364);
11008  return(true);
11009 }
11010 
11011 // ---------------------------------------------------------------------------
11012 
11013 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
11014 // set of legitimate track element IDs, separated by spaces, and at least 1 present
11015 {
11016  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
11017  ExitList.clear();
11018  AnsiString CurrentID = "";
11019 
11020  if(IDSet.Length() == 0)
11021  {
11022  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
11023  Utilities->CallLogPop(1521);
11024  return(false);
11025  }
11026  for(int x = 1; x <= IDSet.Length(); x++)
11027  {
11028  char C = IDSet[x];
11029  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
11030  {
11031  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
11032  Utilities->CallLogPop(1522);
11033  return(false);
11034  }
11035  }
11036  int Pos = IDSet.Pos(' '); // look for the first space
11037 
11038  while(true)
11039  {
11040  if(Pos == 0)
11041  {
11042  CurrentID = IDSet;
11043  IDSet = "";
11044  }
11045  else
11046  {
11047  CurrentID = IDSet.SubString(1, Pos - 1);
11048  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
11049  }
11050  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
11051  if(VecPos == -1)
11052  {
11053  Utilities->CallLogPop(1523);
11054  return(false); // messages given in GetTrackVectorPositionFromString
11055  }
11056  else
11057  {
11058  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
11059  {
11060  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
11061  Utilities->CallLogPop(1524);
11062  return(false);
11063  }
11064  else
11065  {
11066  // first check for duplicates
11067  if(!ExitList.empty())
11068  {
11069  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
11070  {
11071  if(*ELIT == VecPos)
11072  {
11073  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
11074  Utilities->CallLogPop(1532);
11075  return(false);
11076  }
11077  }
11078  }
11079  // of OK add it to the list
11080  ExitList.push_back(VecPos);
11081  }
11082  }
11083  if(IDSet == "")
11084  {
11085  Utilities->CallLogPop(1525);
11086  return(true);
11087  }
11088  else
11089  {
11090  Pos = IDSet.Pos(' '); // look for the next space
11091  }
11092  } // while(true)
11093 }
11094 
11095 // ---------------------------------------------------------------------------
11096 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
11097  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
11098 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
11099 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
11100 // of each item
11101 {
11102  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
11103  int Pos = 0;
11104  AnsiString Remainder = "";
11105  int SemiColonCount = 0;
11106 
11107  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
11108  {
11109  if(TrainInfoStr[x] == ';')
11110  {
11111  SemiColonCount++;
11112  }
11113  }
11114  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
11115  {
11116  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
11117  "'. Should be headcode + optional description for a continuing service;" +
11118  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
11119  Utilities->CallLogPop(880);
11120  return(false);
11121  }
11122  if(SemiColonCount == 0)
11123  {
11124  HeadCode = TrainInfoStr;
11125  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
11126  {
11127  Utilities->CallLogPop(881);
11128  return(false);
11129  }
11130  Utilities->CallLogPop(882);
11131  return(true);
11132  }
11133  if(SemiColonCount == 1) // headcode & description only
11134  {
11135  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11136  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11137  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11138  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
11139  {
11140  Utilities->CallLogPop(883);
11141  return(false);
11142  }
11143  if(Description == "")
11144  {
11145  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11146  Utilities->CallLogPop(884);
11147  return(false);
11148  }
11149  if(Description.Length() > 60)
11150  {
11151  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11152  Utilities->CallLogPop(1157);
11153  return(false);
11154  }
11155  for(int x = 1; x < Description.Length() + 1; x++)
11156  {
11157  if((Description[x] < ' ') || (Description[x] > '~'))
11158  {
11159  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11160  Utilities->CallLogPop(885);
11161  return(false);
11162  }
11163  }
11164  Utilities->CallLogPop(886);
11165  return(true);
11166  }
11167  // if here must have 6 or 7 semicolons
11168  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11169  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11170  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11171  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
11172  {
11173  Utilities->CallLogPop(887);
11174  return(false);
11175  }
11176  Pos = Remainder.Pos(';'); // 2nd delimiter
11177  Description = Remainder.SubString(1, Pos - 1);
11178  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11179  if(Description == "")
11180  {
11181  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11182  Utilities->CallLogPop(888);
11183  return(false);
11184  }
11185  if(Description.Length() > 60)
11186  {
11187  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11188  Utilities->CallLogPop(1158);
11189  return(false);
11190  }
11191  for(int x = 1; x < Description.Length() + 1; x++)
11192  {
11193  if((Description[x] < ' ') || (Description[x] > 126))
11194  {
11195  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11196  Utilities->CallLogPop(889);
11197  return(false);
11198  }
11199  }
11200  Pos = Remainder.Pos(';'); // 3rd delimiter
11201  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
11202 
11203  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11204  if(StartSpeedStr == "")
11205  {
11206  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
11207  Utilities->CallLogPop(890);
11208  return(false);
11209  }
11210  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
11211  {
11212  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
11213  {
11214  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
11215  Utilities->CallLogPop(891);
11216  return(false);
11217  }
11218  }
11219  StartSpeed = StartSpeedStr.ToInt();
11220  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11221  {
11222  StartSpeed = TTrain::MaximumSpeedLimit;
11223  if(!SSHigh) // added at v2.4.0
11224  {
11225  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11226  SSHigh = true;
11227  }
11228  }
11229  Pos = Remainder.Pos(';'); // 4th delimiter
11230  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
11231 
11232  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11233  if(MaxRunningSpeedStr == "")
11234  {
11235  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
11236  Utilities->CallLogPop(892);
11237  return(false);
11238  }
11239  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
11240  {
11241  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
11242  {
11243  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
11244  Utilities->CallLogPop(893);
11245  return(false);
11246  }
11247  }
11248  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
11249  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11250  {
11251  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
11252  if(!MRSHigh) // added at v2.4.0
11253  {
11254  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11255  MRSHigh = true;
11256  }
11257  }
11258  if(MaxRunningSpeed < 10)
11259  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11260  {
11261  MaxRunningSpeed = 10;
11262  if(!MRSLow) // added at v2.4.0
11263  {
11264  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11265  MRSLow = true;
11266  }
11267  }
11268  Pos = Remainder.Pos(';'); // 5th delimiter
11269  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
11270 
11271  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11272  if(MassStr == "")
11273  {
11274  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
11275  Utilities->CallLogPop(895);
11276  return(false);
11277  }
11278  for(int x = 1; x < MassStr.Length() + 1; x++)
11279  {
11280  if((MassStr[x] < '0') || (MassStr[x] > '9'))
11281  {
11282  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
11283  Utilities->CallLogPop(896);
11284  return(false);
11285  }
11286  }
11287  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
11288  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
11289  {
11290  Mass = TTrain::MaximumMassLimit;
11291  if(!MassHigh) // added at v2.4.0
11292  {
11293  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
11294  MassHigh = true;
11295  }
11296  }
11297  if(Mass == 0)
11298  {
11299  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
11300  Utilities->CallLogPop(897);
11301  return(false);
11302  }
11303  Pos = Remainder.Pos(';'); // 6th delimiter
11304  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
11305 
11306  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11307  if(MaxBrakeForceStr == "")
11308  {
11309  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
11310  Utilities->CallLogPop(898);
11311  return(false);
11312  }
11313  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
11314  {
11315  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
11316  {
11317  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
11318  Utilities->CallLogPop(899);
11319  return(false);
11320  }
11321  }
11322  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
11323 
11324  // convert to kg force
11325  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
11326  {
11327  MaxBrakeForce = Mass;
11328  if(!BFHigh) // added at v2.4.0
11329  {
11330  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
11331  BFHigh = true;
11332  }
11333  }
11334  if((MaxBrakeForce / Mass) < 0.01)
11335  {
11336  MaxBrakeForce = Mass * 0.01;
11337  if(!BFLow) // added at v2.4.0
11338  {
11339  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
11340  BFLow = true;
11341  }
11342  }
11343  // convert to m/s/s
11344  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
11345  // now may have just a power entry or power and signaller max. speed
11346  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
11347 
11348  if(SemiColonCount == 6)
11349  {
11350  GrossPowerStr = Remainder;
11351  SignallerSpeedStr = "30"; // default value
11352  }
11353  else // must be 7
11354  {
11355  Pos = Remainder.Pos(';'); // 7th delimiter
11356  GrossPowerStr = Remainder.SubString(1, Pos - 1);
11357  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11358  }
11359  // deal with GrossPower
11360  if(GrossPowerStr == "")
11361  {
11362  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
11363  Utilities->CallLogPop(901);
11364  return(false);
11365  }
11366  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
11367  {
11368  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
11369  {
11370  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
11371  Utilities->CallLogPop(902);
11372  return(false);
11373  }
11374  }
11375 
11376  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
11377 
11378  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
11379  {
11380  GrossPower = TTrain::MaximumPowerLimit;
11381  if(!PwrHigh)
11382  {
11383  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
11384  PwrHigh = true;
11385  }
11386  }
11387  else if(GrossPower == 0) // changed at v2.4.0
11388  {
11389  GrossPower = 0.1;
11390  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
11391  }
11392  else if((GrossPower > 0) && (GrossPower < 10000))
11393  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
11394  {
11395  GrossPower = 10000;
11396  }
11397  PowerAtRail = GrossPower * 0.8;
11398  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
11399 
11400  // deal with SignallerSpeed
11401  if(SignallerSpeedStr == "")
11402  {
11403  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
11404  Utilities->CallLogPop(1771);
11405  return(false);
11406  }
11407  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
11408  {
11409  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
11410  {
11411  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
11412  Utilities->CallLogPop(1769);
11413  return(false);
11414  }
11415  }
11416  SignallerSpeed = SignallerSpeedStr.ToInt();
11417  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
11418  {
11419  SignallerSpeed = TTrain::MaximumSpeedLimit;
11420  if(!SigSHigh)
11421  {
11422  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11423  SigSHigh = true;
11424  }
11425  }
11426  if(SignallerSpeed < 10)
11427  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11428  {
11429  SignallerSpeed = 10;
11430  if(!SigSLow)
11431  {
11432  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11433  SigSLow = true;
11434  }
11435  // Utilities->CallLogPop(1770);
11436  // return false;
11437  }
11438  Utilities->CallLogPop(904);
11439  return(true);
11440 }
11441 
11442 // ---------------------------------------------------------------------------
11443 
11444 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
11445  bool GiveMessages)
11446 {
11447  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
11448  // function checks validity of each item and returns false for error
11449  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
11450  if(OneEntry.Length() < 7)
11451  {
11452  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11453  Utilities->CallLogPop(865);
11454  return(false);
11455  }
11456  int SemiColonCount = 0;
11457 
11458  for(int x = 1; x < OneEntry.Length() + 1; x++)
11459  {
11460  if(OneEntry[x] == ';')
11461  {
11462  SemiColonCount++;
11463  }
11464  }
11465  if(SemiColonCount != 3)
11466  {
11467  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11468  Utilities->CallLogPop(866);
11469  return(false);
11470  }
11471  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
11472  {
11473  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11474  Utilities->CallLogPop(867);
11475  return(false);
11476  }
11477  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
11478  // strip off R;
11479 
11480  int Pos = 0;
11481 
11482  Pos = Remainder.Pos(';');
11483  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
11484 
11485  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11486  if(MinutesStr == "")
11487  {
11488  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
11489  Utilities->CallLogPop(868);
11490  return(false);
11491  }
11492  if(MinutesStr.Length() > 3)
11493  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
11494  {
11495  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
11496  Utilities->CallLogPop(2119);
11497  return(false);
11498  }
11499  for(int x = 1; x < MinutesStr.Length() + 1; x++)
11500  {
11501  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
11502  {
11503  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
11504  Utilities->CallLogPop(869);
11505  return(false);
11506  }
11507  }
11508  RearStartOrRepeatMins = MinutesStr.ToInt();
11509  if(RearStartOrRepeatMins == 0)
11510  {
11511  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
11512  Utilities->CallLogPop(870);
11513  return(false);
11514  }
11515  Pos = Remainder.Pos(';');
11516  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
11517 
11518  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11519  if(DigitsStr == "")
11520  {
11521  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
11522  Utilities->CallLogPop(871);
11523  return(false);
11524  }
11525  for(int x = 1; x < DigitsStr.Length() + 1; x++)
11526  {
11527  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
11528  {
11529  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
11530  Utilities->CallLogPop(872);
11531  return(false);
11532  }
11533  }
11534  if(DigitsStr.Length() > 2)
11535  {
11536  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
11537  Utilities->CallLogPop(873);
11538  return(false);
11539  }
11540  FrontStartOrRepeatDigits = DigitsStr.ToInt();
11541 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
11542  route rather than the service
11543  if(FrontStartOrRepeatDigits == 0)
11544  {
11545  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
11546  Utilities->CallLogPop(874);
11547  return false;
11548  }
11549 */
11550  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
11551  // new for v0.6b for unrestricted headcodes
11552  {
11553  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
11554  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
11555  Utilities->CallLogPop(1889);
11556  return(false);
11557  }
11558  AnsiString NumberStr = Remainder;
11559 
11560  if(NumberStr == "")
11561  {
11562  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
11563  Utilities->CallLogPop(875);
11564  return(false);
11565  }
11566  if(NumberStr.Length() > 4)
11567  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
11568  {
11569  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
11570  Utilities->CallLogPop(2118);
11571  return(false);
11572  }
11573  for(int x = 1; x < NumberStr.Length() + 1; x++)
11574  {
11575  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
11576  // catches negative numbers
11577  {
11578  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
11579  Utilities->CallLogPop(876);
11580  return(false);
11581  }
11582  }
11583  NumberOfRepeats = NumberStr.ToInt();
11584  if(NumberOfRepeats == 0)
11585  {
11586  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
11587  Utilities->CallLogPop(877);
11588  return(false);
11589  }
11590  Utilities->CallLogPop(878);
11591  return(true);
11592 }
11593 
11594 // ---------------------------------------------------------------------------
11595 
11596 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages)
11597 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
11598  vector rather than the timetable
11599  Note also that for unlocated Snt entries the LocationType hasn't yet been set
11600 
11601  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
11602 
11603  For info:-
11604  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
11605  {
11606  public:
11607  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled action entries, null
11609  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
11610  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
11611  int NumberOfRepeats; ///< the number of repeating services
11612  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
11614  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
11616  TExitList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
11617  TTimetableFormatType FormatType; ///< defines the timetable action type
11618  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
11619  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
11620  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
11621  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
11623  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
11625 
11626  // inline function
11627 
11629  TActionVectorEntry() {
11630  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
11631  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
11632  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
11633  Warning = false; SignallerControl = false;
11634  }
11635  };
11636 
11637  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
11638 
11639  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
11640  {
11641  public:
11642  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
11645  double MaxBrakeRate; ///< in metres/sec/sec
11646  double MaxRunningSpeed; ///< in km/h
11647  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
11648  int Mass; ///< in kg
11649  int NumberOfTrains; ///< number of repeats + 1
11650  int SignallerSpeed; ///< in km/h for use when under signaller control
11651  int StartSpeed; ///< in km/h
11652  TActionVector ActionVector; ///< all the actions for the train
11653  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
11654 
11655  //inline function
11656 
11658  TTrainDataEntry()
11659  {
11660  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
11661  }
11662  };
11663 
11664  Allowable successors:-
11665  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
11666  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11667  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11668  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11669  set location, else fails)
11670  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11671  set location, else fails)
11672  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11673  set location, else fails)
11674  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11675  set location, else fails)
11676  Fns -> R only
11677  F-nshs -> Nothing (no repeats permitted)
11678  Fjo -> R only
11679  Frh -> R only
11680  Fer -> R only
11681  Frh-sh -> R only
11682  Fns-sh -> R only
11683  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
11684  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11685  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11686  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11687  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11688  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11689  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11690  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11691  Repeat -> Nothing
11692 
11693  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
11694  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
11695  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
11696  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
11697  Check all times increase or stay same through ActionVector
11698  Cycle through all entries in vector setting arr & dep times based on above list
11699  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
11700  Check locations match the arr & dep TimeLoc entries
11701  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
11702  Make above valid succession checks
11703  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
11704  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
11705  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
11706  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
11707  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
11708  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
11709  Set train info for Sfs & Sns entries
11710  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
11711  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
11712  element at each end, or length of 3 & 1 extra element at either end
11713  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
11714  Check all Cmds have EventTime set & Arr & Dep times = -1
11715  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
11716  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
11717 
11718  Give messages in function if errors detected and clear the vector. Return false for failure.
11719 */
11720 
11721 /* Earlier checks:-
11722  Checks carried out with error messages in this function:-
11723  At least one comma in the line (it's based on a csv file);
11724  No entries following train information;
11725  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11726  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11727  First entry not a start entry;
11728  Train information incomplete before a start entry;
11729  Entry follows a finish entry but doesn't begin with 'R';
11730  SplitEntry returns false in a finish entry - message repeats the entry for information;
11731  Last action entry isn't a finish entry.
11732 
11733  Function returns false with no message if:-
11734  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
11735  time is found at all then an error message is given in the calling function);
11736  SplitTrainInfo returns false (message given in called function);
11737  SplitRepeat returns false (message given in called function).
11738 */{
11739  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
11740  if(TrainDataVector.empty())
11741  {
11742  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
11743  TrainDataVector.clear();
11744  Utilities->CallLogPop(1832);
11745  return(false);
11746  }
11747 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
11748  1) must have at least one actionvector entry
11749  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
11750  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
11751  4) first entry must be a start;
11752  4a) if first entry is Snt and not signallercontrol and second is a finish then it can only be Frh, Fjo or Fer//added for v2.0.0
11753  5) a start must be the first entry;
11754  6) a repeat entry must be the last;
11755  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
11756  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
11757  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
11758 */
11759 
11760  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
11761  {
11762  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11763  if(TrainDataVector.at(x).ActionVector.empty())
11764  {
11765  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
11766  TrainDataVector.clear();
11767  Utilities->CallLogPop(1833);
11768  return(false);
11769  }
11770  }
11771  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
11772  {
11773  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11774  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11775  if(!(AVEntry0.SignallerControl))
11776  {
11777  if(TrainDataVector.at(x).ActionVector.size() == 1)
11778  {
11779  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
11780  TrainDataVector.clear();
11781  Utilities->CallLogPop(1822);
11782  return(false);
11783  }
11784  }
11785  }
11786  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
11787  {
11788  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11789  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11790  if(AVEntry0.SignallerControl)
11791  {
11792  if(TrainDataVector.at(x).ActionVector.size() > 2)
11793  {
11794  SecondPassMessage(GiveMessages,
11795  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
11796  TDEntry.HeadCode);
11797  TrainDataVector.clear();
11798  Utilities->CallLogPop(1837);
11799  return(false);
11800  }
11801  if(TrainDataVector.at(x).ActionVector.size() > 1)
11802  {
11803  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11804  if(AVEntry1.FormatType != Repeat)
11805  {
11806  SecondPassMessage(GiveMessages,
11807  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
11808  TrainDataVector.clear();
11809  Utilities->CallLogPop(1838);
11810  return(false);
11811  }
11812  }
11813  }
11814  }
11815  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
11816  {
11817  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11818  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11819  if(AVEntry0.SequenceType != Start)
11820  {
11821  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
11822  TrainDataVector.clear();
11823  Utilities->CallLogPop(1824);
11824  return(false);
11825  }
11826  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
11827  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
11828  // and others for a located Snt, but those checks done later
11829  {
11830  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11831  // must be a second entry if first not signallercontrol
11832  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
11833  {
11834  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
11835  TDEntry.HeadCode);
11836  TrainDataVector.clear();
11837  Utilities->CallLogPop(2046);
11838  return(false);
11839  }
11840  }
11841  }
11842  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
11843  {
11844  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11845  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11846  {
11847  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11848  if((AVEntry.SequenceType == Start) && (y != 0))
11849  {
11850  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
11851  TrainDataVector.clear();
11852  Utilities->CallLogPop(1825);
11853  return(false);
11854  }
11855  }
11856  }
11857  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
11858  {
11859  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11860  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11861  {
11862  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11863  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
11864  {
11865  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
11866  TrainDataVector.clear();
11867  Utilities->CallLogPop(1826);
11868  return(false);
11869  }
11870  }
11871  }
11872  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
11873  {
11874  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11875  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11876  {
11877  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11878  if((y == 0) && AVEntry.SignallerControl)
11879  {
11880  break;
11881  }
11882  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
11883  {
11884  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
11885  {
11886  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
11887  TrainDataVector.clear();
11888  Utilities->CallLogPop(1827);
11889  return(false);
11890  }
11891  if(AVEntry.FormatType == Repeat)
11892  {
11893  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
11894  if(LastAVEntry.SequenceType != Finish)
11895  {
11896  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
11897  TrainDataVector.clear();
11898  Utilities->CallLogPop(1828);
11899  return(false);
11900  }
11901  }
11902  }
11903  }
11904  }
11905  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
11906  {
11907  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11908  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11909  {
11910  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11911  if(AVEntry.SequenceType == Finish)
11912  {
11913  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
11914  {
11915  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
11916  TrainDataVector.clear();
11917  Utilities->CallLogPop(1829);
11918  return(false);
11919  }
11920  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
11921  {
11922  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
11923  {
11924  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
11925  TDEntry.HeadCode);
11926  TrainDataVector.clear();
11927  Utilities->CallLogPop(1830);
11928  return(false);
11929  }
11930  }
11931  }
11932  }
11933  }
11934 
11935  // end of new preliminary checks
11936 
11937  // check ActionVector present and check start event successor validity
11938  // For Snt & Snt-sh set location if stopped, don't set any times yet
11939  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11940  {
11941  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11942  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11943  // use reference so can change internals where necessary
11944  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
11945  {
11946  AnsiString LocationName = "";
11947  if(IsSNTEntryLocated(0, TDEntry, LocationName))
11948  // it is at a location
11949  {
11950  if(TDEntry.StartSpeed == 0) // stopped
11951  {
11952  AVEntry0.LocationName = LocationName;
11953  AVEntry0.LocationType = AtLocation;
11954  // check successor validity for located Snt that isn't a SignallerControl entry
11955  if(!AVEntry0.SignallerControl)
11956  {
11957  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11958  // at least 2 entries present checked in integrity check so (1) valid
11959  if(!AtLocSuccessor(AVEntry1))
11960  {
11961  // Frh following Snt-sh will return false in location check, so no need to check here
11962  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
11963  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
11964  TrainDataVector.clear();
11965  Utilities->CallLogPop(523);
11966  return(false);
11967  }
11968  }
11969  }
11970  else
11971  {
11972  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
11973  TDEntry.HeadCode);
11974  TrainDataVector.clear();
11975  Utilities->CallLogPop(791);
11976  return(false);
11977  }
11978  }
11979  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
11980  {
11981  if(AVEntry0.Command == "Snt-sh")
11982  {
11983  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
11984  TrainDataVector.clear();
11985  Utilities->CallLogPop(1042);
11986  return(false);
11987  }
11988  AVEntry0.LocationType = EnRoute;
11989  if(!AVEntry0.SignallerControl)
11990  {
11991  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11992  // at least 2 entries checked in integrity check so (1) valid
11993  if(!MovingSuccessor(AVEntry1))
11994  {
11995  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
11996  TDEntry.HeadCode);
11997  TrainDataVector.clear();
11998  Utilities->CallLogPop(790);
11999  return(false);
12000  }
12001  }
12002  }
12003  }
12004  // check other start successors
12005  else if(AVEntry0.SequenceType == Start)
12006  {
12007  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12008  // at least 2 entries present checked in integrity check so (1) valid
12009  if(!AtLocSuccessor(AVEntry1))
12010  {
12011  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
12012  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12013  TrainDataVector.clear();
12014  Utilities->CallLogPop(793);
12015  return(false);
12016  }
12017  }
12018  }
12019 
12020  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
12021  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12022  {
12023  bool FoundFlag = false;
12024  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12025  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
12026  // use reference so can change internals
12027  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
12028  {
12029  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
12030  {
12031  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
12032  if(AVEntry2.FormatType == TimeLoc)
12033  {
12034  FoundFlag = true;
12035  AVEntry.LocationName = AVEntry2.LocationName;
12036  break;
12037  }
12038  }
12039  if(!FoundFlag)
12040  {
12041  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
12042  TDEntry.HeadCode);
12043  TrainDataVector.clear();
12044  Utilities->CallLogPop(851);
12045  return(false);
12046  }
12047  }
12048  }
12049 
12050  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
12051  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12052  {
12053  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12054  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12055  {
12056  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12057  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
12058  {
12059  if(AVEntry.LocationName == "")
12060  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
12061  {
12062  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
12063  ": an event should have had a location name associated with it but it could not be found");
12064  TrainDataVector.clear();
12065  Utilities->CallLogPop(1831);
12066  return(false);
12067  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
12068  }
12069  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
12070  {
12071  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
12072  // use reference so can change internals where necessary
12073  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
12074  {
12075  AVEntry2.LocationName = AVEntry.LocationName;
12076  }
12077  else
12078  {
12079  break;
12080  }
12081  }
12082  }
12083  }
12084  }
12085  // all location names now set
12086 
12087  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
12088  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12089  {
12090  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12091  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12092  {
12093  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12094  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
12095  {
12096  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
12097  // i.e at least one more, must be a repeat
12098  {
12099  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12100  {
12101  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
12102  TrainDataVector.clear();
12103  Utilities->CallLogPop(798);
12104  return(false);
12105  }
12106  }
12107  }
12108  if(AVEntry.Command == "F-nshs")
12109  {
12110  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12111  // i.e has to be the last
12112  {
12113  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
12114  TrainDataVector.clear();
12115  Utilities->CallLogPop(1049);
12116  return(false);
12117  }
12118  }
12119  if(AVEntry.Command == "pas")
12120  {
12121  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12122  {
12123  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
12124  TrainDataVector.clear();
12125  Utilities->CallLogPop(1518);
12126  return(false);
12127  }
12128  }
12129  if(AVEntry.Command == "jbo")
12130  {
12131  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12132  {
12133  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
12134  TrainDataVector.clear();
12135  Utilities->CallLogPop(800);
12136  return(false);
12137  }
12138  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12139  if(!AtLocSuccessor(AVEntry2))
12140  {
12141  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
12142  ". The event isn't valid for a stationary train.");
12143  TrainDataVector.clear();
12144  Utilities->CallLogPop(801);
12145  return(false);
12146  }
12147  }
12148  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
12149  {
12150  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12151  {
12152  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
12153  TrainDataVector.clear();
12154  Utilities->CallLogPop(802);
12155  return(false);
12156  }
12157  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12158  if(!AtLocSuccessor(AVEntry2))
12159  {
12160  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
12161  ". The event isn't valid for a stationary train.");
12162  TrainDataVector.clear();
12163  Utilities->CallLogPop(803);
12164  return(false);
12165  }
12166  }
12167  if(AVEntry.Command == "cdt")
12168  {
12169  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12170  {
12171  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
12172  TrainDataVector.clear();
12173  Utilities->CallLogPop(804);
12174  return(false);
12175  }
12176  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12177  if(!AtLocSuccessor(AVEntry2))
12178  {
12179  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
12180  ". The event isn't valid for a stationary train.");
12181  TrainDataVector.clear();
12182  Utilities->CallLogPop(805);
12183  return(false);
12184  }
12185  }
12186  if(AVEntry.FormatType == TimeTimeLoc)
12187  {
12188  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12189  {
12190  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
12191  TrainDataVector.clear();
12192  Utilities->CallLogPop(806);
12193  return(false);
12194  }
12195  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12196  if(!MovingSuccessor(AVEntry2))
12197  {
12198  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
12199  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
12200  TrainDataVector.clear();
12201  Utilities->CallLogPop(807);
12202  return(false);
12203  }
12204  }
12205  if(AVEntry.FormatType == PassTime)
12206  {
12207  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12208  {
12209  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
12210  TrainDataVector.clear();
12211  Utilities->CallLogPop(1530);
12212  return(false);
12213  }
12214  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12215  if(!MovingSuccessor(AVEntry2))
12216  {
12217  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
12218  ". The event isn't valid for a moving train.");
12219  TrainDataVector.clear();
12220  Utilities->CallLogPop(1531);
12221  return(false);
12222  }
12223  }
12224  if(AVEntry.FormatType == Repeat)
12225  {
12226  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12227  {
12228  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
12229  TrainDataVector.clear();
12230  Utilities->CallLogPop(808);
12231  return(false);
12232  }
12233  }
12234  }
12235  }
12236 
12237  // set arrival & departure times for TimeLocs & set their EventTimes to -1
12238  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12239  {
12240  bool LastEntryIsAnArrival = false;
12241  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12242  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
12243  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12244  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12245  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
12246  {
12247  LastEntryIsAnArrival = false;
12248  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12249  {
12250  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12251  if(AVEntry.FormatType == TimeLoc)
12252  {
12253  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12254  {
12255  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12256  }
12257  if(LastEntryIsAnArrival)
12258  {
12259  AVEntry.DepartureTime = AVEntry.EventTime;
12260  AVEntry.EventTime = TDateTime(-1);
12261  LastEntryIsAnArrival = false;
12262  }
12263  else // last entry a departure
12264  {
12265  AVEntry.ArrivalTime = AVEntry.EventTime;
12266  AVEntry.EventTime = TDateTime(-1);
12267  LastEntryIsAnArrival = true;
12268  }
12269  }
12270  }
12271  }
12272  else // all others stopped at beginning
12273  {
12274  LastEntryIsAnArrival = true;
12275  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12276  {
12277  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12278  if(AVEntry.FormatType == TimeLoc)
12279  {
12280  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12281  {
12282  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12283  }
12284  if(LastEntryIsAnArrival)
12285  {
12286  AVEntry.DepartureTime = AVEntry.EventTime;
12287  AVEntry.EventTime = TDateTime(-1);
12288  LastEntryIsAnArrival = false;
12289  }
12290  else // last entry a departure
12291  {
12292  AVEntry.ArrivalTime = AVEntry.EventTime;
12293  AVEntry.EventTime = TDateTime(-1);
12294  LastEntryIsAnArrival = true;
12295  }
12296  }
12297  }
12298  }
12299  }
12300  // perform remaining successor checks for TimeLocs
12301  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12302  {
12303  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12304  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12305  {
12306  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12307  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
12308  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
12309  {
12310  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12311  {
12312  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
12313  TrainDataVector.clear();
12314  Utilities->CallLogPop(809);
12315  return(false);
12316  }
12317  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12318  if(!AtLocSuccessor(AVEntry2))
12319  {
12320  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
12321  ". The event isn't valid for a stationary train.");
12322  TrainDataVector.clear();
12323  Utilities->CallLogPop(810);
12324  return(false);
12325  }
12326  }
12327  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
12328  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
12329  {
12330  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12331  {
12332  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
12333  TrainDataVector.clear();
12334  Utilities->CallLogPop(811);
12335  return(false);
12336  }
12337  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12338  if(!MovingSuccessor(AVEntry2))
12339  {
12340  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
12341  ". The event isn't valid for a moving train.");
12342  TrainDataVector.clear();
12343  Utilities->CallLogPop(812);
12344  return(false);
12345  }
12346  }
12347  }
12348  }
12349 
12350  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
12351  // & repeats have no times set
12352  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12353  {
12354  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12355  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12356  {
12357  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12358  if(AVEntry.FormatType == TimeLoc)
12359  {
12360  if(AVEntry.EventTime != TDateTime(-1))
12361  {
12362  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12363  }
12364  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
12365  {
12366  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
12367  }
12368  }
12369  if(AVEntry.FormatType == TimeTimeLoc)
12370  {
12371  if(AVEntry.EventTime != TDateTime(-1))
12372  {
12373  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12374  }
12375  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
12376  {
12377  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
12378  }
12379  }
12380  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
12381  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
12382  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
12383  {
12384  if(AVEntry.EventTime == TDateTime(-1))
12385  {
12386  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
12387  }
12388  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12389  {
12390  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
12391  }
12392  }
12393  if(AVEntry.FormatType == Repeat)
12394  {
12395  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12396  {
12397  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
12398  }
12399  }
12400  }
12401  }
12402 
12403  // check times stay same or increase, note that can have time of 0 if include midnight
12404  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12405  {
12406  TDateTime CurrentTime = TTClockTime; // the timetable start time
12407  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12408  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12409  {
12410  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12411  if(AVEntry.FormatType == Repeat)
12412  {
12413  break;
12414  }
12415  if(AVEntry.FormatType == FinRemHere)
12416  {
12417  break;
12418  }
12419  if(AVEntry.FormatType == TimeTimeLoc)
12420  {
12421  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
12422  {
12423  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
12424  TDEntry.HeadCode);
12425  TrainDataVector.clear();
12426  Utilities->CallLogPop(813);
12427  return(false);
12428  }
12429  if(AVEntry.ArrivalTime < CurrentTime)
12430  {
12431  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
12432  TDEntry.HeadCode);
12433  TrainDataVector.clear();
12434  Utilities->CallLogPop(814);
12435  return(false);
12436  }
12437  CurrentTime = AVEntry.DepartureTime;
12438  continue;
12439  }
12440  if(AVEntry.FormatType == TimeLoc)
12441  {
12442  if(AVEntry.ArrivalTime >= TDateTime(0))
12443  {
12444  if(AVEntry.ArrivalTime < CurrentTime)
12445  {
12446  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12447  TrainDataVector.clear();
12448  Utilities->CallLogPop(815);
12449  return(false);
12450  }
12451  CurrentTime = AVEntry.ArrivalTime;
12452  }
12453  else
12454  {
12455  if(AVEntry.DepartureTime < CurrentTime)
12456  // both may be 0 legitimately so must allow for this
12457  {
12458  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12459  TrainDataVector.clear();
12460  Utilities->CallLogPop(816);
12461  return(false);
12462  }
12463  CurrentTime = AVEntry.DepartureTime;
12464  }
12465  continue;
12466  }
12467  if(AVEntry.EventTime < CurrentTime)
12468  // all others have EventTime set
12469  {
12470  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
12471  ", may be before timetable start time");
12472  TrainDataVector.clear();
12473  Utilities->CallLogPop(835);
12474  return(false);
12475  }
12476  CurrentTime = AVEntry.EventTime;
12477  continue;
12478  }
12479  }
12480 
12481  // check locations consistent
12482  AnsiString LastLocationName = "";
12483 
12484  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12485  {
12486  bool LastEntryIsAnArrival = false;
12487  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12488  // first deal with moving Snt entries (all else stopped)
12489  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
12490  {
12491  LastEntryIsAnArrival = false;
12492  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
12493  if(LastLocationName != "")
12494  {
12495  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
12496  }
12497  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
12498  y++) // note that immediate successor to a moving Snt can only be a Moving type
12499  {
12500  // if it's a SignallerControl entry then the condition isn't met
12501  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12502  if(AVEntry.FormatType == Repeat)
12503  {
12504  break; // repeat = reached end (+allows repeat after signaller controlled entry)
12505  }
12506  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12507  {
12508  if(AVEntry.LocationName != LastLocationName)
12509  {
12510  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12511  AVEntry.Command);
12512  TrainDataVector.clear();
12513  Utilities->CallLogPop(823);
12514  return(false);
12515  }
12516  }
12517  else if(AVEntry.FormatType == TimeCmd)
12518  // cdt is the only TimeCmd
12519  {
12520  if(AVEntry.LocationName != LastLocationName)
12521  {
12522  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12523  AVEntry.Command);
12524  TrainDataVector.clear();
12525  Utilities->CallLogPop(824);
12526  return(false);
12527  }
12528  }
12529  else if(AVEntry.FormatType == TimeTimeLoc)
12530  {
12531  if((AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12532  // last entry must be a departure or would have failed earlier
12533  {
12534  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12536 // SecondPassMessage(GiveMessages,
12537 // "Error in timetable - a location event in a timed arrival and departure is the same as the last location for: " + TDEntry.HeadCode);
12538 // TrainDataVector.clear();
12539 // Utilities->CallLogPop(825);
12540 // return false;
12541  }
12542  LastLocationName = AVEntry.LocationName;
12543  LastEntryIsAnArrival = false;
12544  }
12545  else if(AVEntry.FormatType == TimeLoc)
12546  {
12547  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12548  {
12549  SecondPassMessage(GiveMessages,
12550  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12551  TrainDataVector.clear();
12552  Utilities->CallLogPop(826);
12553  return(false);
12554  }
12555  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
12556  {
12557  SecondPassMessage(GiveMessages,
12558  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
12559  TrainDataVector.clear();
12560  Utilities->CallLogPop(827);
12561  return(false);
12562  }
12563  LastLocationName = AVEntry.LocationName;
12564  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12565  }
12566  }
12567  }
12568  else // all stationary starting entries
12569  {
12570  LastEntryIsAnArrival = true;
12571  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
12572  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12573  {
12574  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12575  if(AVEntry.FormatType == Repeat)
12576  {
12577  break;
12578  }
12579  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12580  // no need to add anything for shuttle starts since they are at loc (0) anyway
12581  {
12582  if(AVEntry.LocationName != LastLocationName)
12583  {
12584  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12585  AVEntry.Command);
12586  TrainDataVector.clear();
12587  Utilities->CallLogPop(828);
12588  return(false);
12589  }
12590  }
12591  else if(AVEntry.FormatType == TimeCmd)
12592  // cdt is the only TimeCmd
12593  {
12594  if(AVEntry.LocationName != LastLocationName)
12595  {
12596  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12597  AVEntry.Command);
12598  TrainDataVector.clear();
12599  Utilities->CallLogPop(829);
12600  return(false);
12601  }
12602  }
12603  else if(AVEntry.FormatType == TimeTimeLoc)
12604  {
12605  if((AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12606  // last entry must be a departure or would have failed earlier
12607  {
12608  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12610 // SecondPassMessage(GiveMessages,
12611 // "Error in timetable - a location event in a timed arrival and departure is the same as the last location for: " + TDEntry.HeadCode);
12612 // TrainDataVector.clear();
12613 // Utilities->CallLogPop(830);
12614 // return false;
12615  }
12616  LastLocationName = AVEntry.LocationName;
12617  LastEntryIsAnArrival = false;
12618  }
12619  else if(AVEntry.FormatType == TimeLoc)
12620  {
12621  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12622  {
12623  SecondPassMessage(GiveMessages,
12624  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12625  TrainDataVector.clear();
12626  Utilities->CallLogPop(831);
12627  return(false);
12628  }
12629  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
12630  {
12631  SecondPassMessage(GiveMessages,
12632  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
12634 // TrainDataVector.clear();
12635 // Utilities->CallLogPop(832);
12636 // return false;
12637  }
12638  LastLocationName = AVEntry.LocationName;
12639  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12640  }
12641  }
12642  }
12643  }
12644 
12645  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
12646  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
12647  AnsiString LocationNameToBeChecked = "";
12648 
12649  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12650  {
12651  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12652  unsigned int y = 0;
12653  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
12654  // first discard unlocated Snt entries as they don't have location name set
12655  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12656  {
12657  y = 1;
12658  }
12659  while(y < TDEntry.ActionVector.size())
12660  // need to check each location name separately in turn, skipped for SignallerControl entries
12661  // if y == 1
12662  {
12663  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
12664  {
12665  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12666  }
12667  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
12668  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
12669  {
12670  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
12671  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
12672  {
12673  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12674  }
12675  if(AVEntry.Command == "cdt")
12676  {
12677  break; // out of the 'z' loop since the check is only valid up to a change of direction
12678  }
12679  if(AVEntry.LocationName == LocationNameToBeChecked)
12680  {
12681  continue; // keep going while name same
12682  }
12683  if(AVEntry.LocationName != LocationNameToBeChecked)
12684  // if name different check forwards to see if repeats
12685  {
12686  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
12687  {
12688  if(TDEntry.ActionVector.at(a).Command == "cdt")
12689  {
12690  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
12691  }
12692  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12693  {
12694  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12696  // SecondPassMessage(GiveMessages, "Error in timetable - a location entry appears twice inappropriately for: " + TDEntry.HeadCode);
12697  // TrainDataVector.clear();
12698  // Utilities->CallLogPop(833);
12699  // return false;
12700  }
12701  }
12702  break; // out of the 'z' loop since have checked 'a' as far as need to
12703  }
12704  }
12705  y++;
12706  }
12707  }
12708 
12709  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
12710  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12711  {
12712  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12713  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12714  {
12715  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12716  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
12717  {
12718  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
12719  }
12720  AnsiString LocName = "";
12721  // dummy, only used so can call IsSNTEntryLocated
12722  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
12723  {
12724  if(AVEntry.LocationName == "")
12725  {
12726  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
12727  }
12728  }
12729  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
12730  {
12731  if(AVEntry.LocationName != "")
12732  {
12733  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
12734  }
12735  }
12736  }
12737  }
12738 
12739 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12740  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12741  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
12742 
12743  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
12744  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
12745 
12746  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
12747  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
12748  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
12749 */
12750  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
12751  {
12752  // non-shuttles & non-repeating links separately, but don't check that there isn't a
12753  // duplicate between a non-repeating shuttle and another - leave original tests in as
12754  // these also set the pointers
12755  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12756  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12757  {
12758  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12759  if(AVEntry.OtherHeadCode != "")
12760  {
12761  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
12762  {
12763  Utilities->CallLogPop(1584);
12764  return(false); // error message given in called function
12765  }
12766  }
12767  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12768  {
12769  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
12770  {
12771  Utilities->CallLogPop(1585);
12772  return(false); // error message given in called function
12773  }
12774  }
12775  }
12776  }
12777 
12778  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12779  {
12780  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12781  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12782  {
12783  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12784  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12785  {
12786  if(AVEntry.OtherHeadCode != "")
12787  {
12788  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
12789  // false = non-shuttle
12790  {
12791  Utilities->CallLogPop(864);
12792  return(false); // error message given in called function
12793  }
12794  }
12795  }
12796  }
12797  }
12798 
12799  // now repeat the check just for the shuttles
12800  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12801  {
12802  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12803  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12804  {
12805  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12806  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
12807  {
12808  if(AVEntry.OtherHeadCode != "")
12809  {
12810  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
12811  // true = shuttle
12812  {
12813  Utilities->CallLogPop(1100);
12814  return(false); // error message given in called function
12815  }
12816  }
12817  }
12818  }
12819  }
12820 
12821  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
12822  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12823  {
12824  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12825  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12826  {
12827  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12828  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12829  {
12831  {
12832  Utilities->CallLogPop(1060);
12833  return(false); // error message given in called function
12834  }
12835  }
12836  }
12837  }
12838 
12839  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
12840  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
12841  // don't ever need to and as designed would skip repeats
12842  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12843  {
12844  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12845  {
12846  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12847  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
12848  {
12849  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
12850  {
12851  Utilities->CallLogPop(1090);
12852  return(false); // error message given in called function
12853  }
12854  }
12855  }
12856  }
12857 
12858  // check all entries have all types set to something
12859  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12860  {
12861  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12862  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12863  {
12864  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12865  if(AVEntry.FormatType == NoFormat)
12866  {
12867  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
12868  }
12869  else if(AVEntry.SequenceType == NoSequence)
12870  {
12871  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
12872  }
12873  else if(AVEntry.LocationType == NoLocation)
12874  {
12875  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
12876  }
12877  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
12878  {
12879  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
12880  }
12881  }
12882  }
12883 
12884  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
12885  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12886  {
12887  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12888  // non-const reference so can alter content
12889  TTrainOperatingData TData;
12890  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
12891  if(LastAVEntry.FormatType == Repeat) // check if a repeat
12892  {
12893 /*
12894  class TTrainOperatingData
12895  {
12896  public:
12897  int TrainID; - default, set at construction
12898  TActionEventType EventReported; used during operation
12899  TRunningEntry RunningEntry; - default, set at construction
12900  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
12901  };
12902 */
12903  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
12904  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
12905  {
12906  TDEntry.TrainOperatingDataVector.push_back(TData);
12907  }
12908  }
12909  else
12910  {
12911  TDEntry.NumberOfTrains = 1;
12912  }
12913  }
12914 
12915  // check that don't include any Continuation names
12916  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12917  {
12918  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12919  {
12920  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
12921  AnsiString HC = TrainDataVector.at(x).HeadCode;
12922  if(LocName != "")
12923  {
12924  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
12925  {
12926  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
12927  TrainDataVector.clear();
12928  Utilities->CallLogPop(1578);
12929  return(false);
12930  }
12931  }
12932  }
12933  }
12934 
12935  // check that all repeat times below 96h
12936  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12937  {
12938  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
12939  int IncMinutes = 0;
12940  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
12941  {
12942  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
12943  }
12944  else
12945  {
12946  continue; // basic times already checked in CheckTimeValidity
12947  }
12948  AnsiString HC = TrainDataVector.at(x).HeadCode;
12949  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12950  {
12951  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
12952  {
12953  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12954  {
12955  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
12956  TrainDataVector.clear();
12957  Utilities->CallLogPop(1818);
12958  return(false);
12959  }
12960  }
12961  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
12962  {
12963  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12964  // 3d 23h 59m = 3.9993055556
12965  {
12966  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12967  TrainDataVector.clear();
12968  Utilities->CallLogPop(1819);
12969  return(false);
12970  }
12971  }
12972  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
12973  {
12974  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12975  // 3d 23h 59m = 3.9993055556
12976  {
12977  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12978  TrainDataVector.clear();
12979  Utilities->CallLogPop(1820);
12980  return(false);
12981  }
12982  }
12983  }
12984  }
12985 
12986  // Now that all set up change any extended headcodes back to ordinary headcodes
12987  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12988  {
12989  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
12990  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12991  {
12992  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
12993  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
12994  }
12995  }
12996 
12997  // SaveTrainDataVectorToFile(0);//test
12999  Utilities->CallLogPop(782);
13000  return(true);
13001 }
13002 
13003 // ---------------------------------------------------------------------------
13004 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
13006 {
13007  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
13008 }
13009 
13010 // ---------------------------------------------------------------------------
13011 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
13013 {
13014  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
13015  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
13016  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
13017 }
13018 
13019 // ---------------------------------------------------------------------------
13020 
13021 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
13022 {
13023  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
13024  if(HeadCode.Length() > 4) // ignore otherwise
13025  {
13026  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
13027  }
13028  Utilities->CallLogPop(1593);
13029 }
13030 
13031 // ---------------------------------------------------------------------------
13032 
13033 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
13034 {
13035  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
13036  SecondHeadCode);
13037  int ForwardCount = 0;
13038  int ReverseCount = 0;
13039 
13040  if(MainHeadCode == SecondHeadCode)
13041  {
13042  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
13043  TrainDataVector.clear();
13044  Utilities->CallLogPop(1594);
13045  return(false);
13046  }
13047  // forward check
13048  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13049  {
13050  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13051  if(TDEntry.HeadCode == MainHeadCode)
13052  {
13053  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13054  {
13055  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13056  if(AVEntry.OtherHeadCode == SecondHeadCode)
13057  {
13058  ForwardCount++;
13059  }
13060  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
13061  // need own check in case both 'Other' & 'NonRepeating' have same headcode
13062  {
13063  ForwardCount++;
13064  }
13065  }
13066  }
13067  }
13068  if(ForwardCount == 0)
13069  // this is an exception because the headcodes are selected in the same order as the forward check
13070  {
13071  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
13072  }
13073  if(ForwardCount > 2)
13074  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
13075  {
13076  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
13077  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13078  TrainDataVector.clear();
13079  Utilities->CallLogPop(1587);
13080  return(false);
13081  }
13082  // reverse check
13083  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13084  {
13085  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13086  if(TDEntry.HeadCode == SecondHeadCode)
13087  {
13088  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13089  {
13090  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13091  if(AVEntry.OtherHeadCode == MainHeadCode)
13092  {
13093  ReverseCount++;
13094  }
13095  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13096  {
13097  ReverseCount++;
13098  }
13099  }
13100  }
13101  }
13102 
13103  if(ReverseCount == 0)
13104  {
13105  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
13106  TrainDataVector.clear();
13107  Utilities->CallLogPop(1588);
13108  return(false);
13109  }
13110  if(ReverseCount > 2)
13111  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
13112  {
13113  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
13114  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13115  TrainDataVector.clear();
13116  Utilities->CallLogPop(1589);
13117  return(false);
13118  }
13119  if(ForwardCount != ReverseCount)
13120  {
13121  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
13122  " than the other way round");
13123  TrainDataVector.clear();
13124  Utilities->CallLogPop(1610);
13125  return(false);
13126  }
13127  Utilities->CallLogPop(1590);
13128  return(true);
13129 }
13130 
13131 // ---------------------------------------------------------------------------
13132 
13133 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
13134 /* Return false for no find or more than one find, check correct types of link
13135  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
13136  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
13137  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
13138  Then do the same in reverse.
13139  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
13140  if main is Fns other must be Sns; if main is jbo other must be Fjo.
13141  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
13142  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
13143  for Sfs & Sns services. Finally check the repeat entries if present are consistent
13144 
13145  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
13146  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
13147 
13148  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
13149 */
13150 
13151 {
13152  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
13153  int ForwardCount = 0;
13154  int ReverseCount = 0;
13155  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13156  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13157  TTrainDataEntry *MainTrainDataPtr = 0;
13158  TTrainDataEntry *OtherTrainDataPtr = 0;
13159 
13160  // forward check
13161  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13162  {
13163  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13164  if(TDEntry.HeadCode == MainHeadCode)
13165  {
13166  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13167  {
13168  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13169  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13170  {
13171  if(AVEntry.OtherHeadCode == OtherHeadCode)
13172  {
13173  MainTrainDataPtr = &TrainDataVector.at(x);
13174  ForwardEntryPtr = &AVEntry;
13175  ForwardCount++;
13176  ForwardTDVectorNumber = x;
13177  }
13178  }
13179  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
13180  (AVEntry.Command == "Frh-sh")))
13181  {
13182  if(AVEntry.OtherHeadCode == OtherHeadCode)
13183  {
13184  MainTrainDataPtr = &TrainDataVector.at(x);
13185  ForwardEntryPtr = &AVEntry;
13186  ForwardCount++;
13187  ForwardTDVectorNumber = x;
13188  }
13189  }
13190  }
13191  }
13192  }
13193  if(ForwardCount == 0)
13194  // this is an exception because the headcodes are selected in the same order as the forward check
13195  {
13196  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
13197  }
13198  if(ForwardCount > 1)
13199  {
13200  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
13201  MainHeadCode);
13202  TrainDataVector.clear();
13203  Utilities->CallLogPop(836);
13204  return(false);
13205  }
13206  // reverse check
13207  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13208  {
13209  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13210  if(TDEntry.HeadCode == OtherHeadCode)
13211  {
13212  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13213  {
13214  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13215  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13216  {
13217  if(AVEntry.OtherHeadCode == MainHeadCode)
13218  {
13219  OtherTrainDataPtr = &TrainDataVector.at(x);
13220  ReverseCount++;
13221  ReverseEntryPtr = &AVEntry;
13222  ReverseTDVectorNumber = x;
13223  }
13224  }
13225  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
13226  {
13227  if(AVEntry.OtherHeadCode == MainHeadCode)
13228  {
13229  OtherTrainDataPtr = &TrainDataVector.at(x);
13230  ReverseCount++;
13231  ReverseEntryPtr = &AVEntry;
13232  ReverseTDVectorNumber = x;
13233  }
13234  }
13235  }
13236  }
13237  }
13238 
13239  if(ReverseCount == 0)
13240  {
13241  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
13242  TrainDataVector.clear();
13243  Utilities->CallLogPop(837);
13244  return(false);
13245  }
13246  if(ReverseCount > 1)
13247  {
13248  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13249  OtherHeadCode);
13250  TrainDataVector.clear();
13251  Utilities->CallLogPop(838);
13252  return(false);
13253  }
13254  // these will all be false for !Shuttle
13255  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
13256  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
13257  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
13258  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
13259 
13260  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
13261  {
13262  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
13263  TrainDataVector.clear();
13264  Utilities->CallLogPop(1058);
13265  return(false);
13266  }
13267  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
13268  {
13269  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
13270  TrainDataVector.clear();
13271  Utilities->CallLogPop(1059);
13272  return(false);
13273  }
13274  if(ForwardEntryPtr->LocationName == "")
13275  {
13276  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13277  ". One or other service does not have a location set");
13278  TrainDataVector.clear();
13279  Utilities->CallLogPop(526);
13280  return(false);
13281  }
13282  if(ReverseEntryPtr->LocationName == "")
13283  {
13284  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13285  ". One or other service does not have a location set");
13286  TrainDataVector.clear();
13287  Utilities->CallLogPop(527);
13288  return(false);
13289  }
13290  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13291  {
13292  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13293  " is at a different location to the referencing train " + MainHeadCode);
13294  TrainDataVector.clear();
13295  Utilities->CallLogPop(842);
13296  return(false);
13297  }
13298  // ignore shuttle repeat links for first time check
13299  if(!Shuttle)
13300  {
13301  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13302  {
13303  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13304  " has a different event time to the referencing train " + MainHeadCode);
13305  TrainDataVector.clear();
13306  Utilities->CallLogPop(525);
13307  return(false);
13308  }
13309  }
13310  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
13311  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
13312  if(ForwardShuttleStart && ReverseShuttleFinish)
13313  // Shuttle must be true if these are true
13314  {
13315  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
13316  {
13317  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
13318  " first repeat restart time not consistent with finish service " + OtherHeadCode);
13319  TrainDataVector.clear();
13320  Utilities->CallLogPop(1055);
13321  return(false);
13322  }
13323  }
13324  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
13325  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
13326  {
13327  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13328  {
13329  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
13330  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13331  TrainDataVector.clear();
13332  Utilities->CallLogPop(528);
13333  return(false);
13334  }
13335  }
13336  if(ReverseEntryPtr->Command == "Fjo")
13337  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
13338  {
13339  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13340  {
13341  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
13342  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13343  TrainDataVector.clear();
13344  Utilities->CallLogPop(862);
13345  return(false);
13346  }
13347  }
13348  if(ReverseEntryPtr->Command == "Fns")
13349  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
13350  {
13351  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13352  {
13353  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
13354  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13355  TrainDataVector.clear();
13356  Utilities->CallLogPop(529);
13357  return(false);
13358  }
13359  }
13360  if(ForwardEntryPtr->Command == "Sfs")
13361  {
13362  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
13363  {
13364  SecondPassMessage(GiveMessages,
13365  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
13366  MainHeadCode);
13367  TrainDataVector.clear();
13368  Utilities->CallLogPop(530);
13369  return(false);
13370  }
13371  }
13372  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
13373  {
13374  if(ReverseEntryPtr->Command != "Sfs")
13375  {
13376  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
13377  MainHeadCode);
13378  TrainDataVector.clear();
13379  Utilities->CallLogPop(839);
13380  return(false);
13381  }
13382  else
13383  {
13384  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
13385  {
13386  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
13387  TrainDataVector.clear();
13388  Utilities->CallLogPop(849);
13389  return(false);
13390  }
13391  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
13392  {
13393  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
13394  TrainDataVector.clear();
13395  Utilities->CallLogPop(850);
13396  return(false);
13397  }
13398  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
13399  {
13400  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
13401  TrainDataVector.clear();
13402  Utilities->CallLogPop(846);
13403  return(false);
13404  }
13405  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13406  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13407  if(OtherTrainDataPtr->Description == "")
13408  {
13409  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13410  }
13411  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
13412  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13413  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13414  }
13415  }
13416  if(ForwardEntryPtr->Command == "Sns")
13417  {
13418  if(ReverseEntryPtr->Command != "Fns")
13419  {
13420  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
13421  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
13422  TrainDataVector.clear();
13423  Utilities->CallLogPop(531);
13424  return(false);
13425  }
13426  }
13427  if(ForwardEntryPtr->Command == "Fns")
13428  {
13429  if(ReverseEntryPtr->Command != "Sns")
13430  {
13431  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
13432  " and forms a new service with headcode " + OtherHeadCode);
13433  TrainDataVector.clear();
13434  Utilities->CallLogPop(840);
13435  return(false);
13436  }
13437  else
13438  {
13439  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13440  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13441  if(OtherTrainDataPtr->Description == "")
13442  {
13443  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13444  }
13445  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13446  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13447  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13448  }
13449  }
13450  if(ForwardEntryPtr->Command == "jbo")
13451  {
13452  if(ReverseEntryPtr->Command != "Fjo")
13453  {
13454  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
13455  " and is joined by a train with headcode " + OtherHeadCode);
13456  TrainDataVector.clear();
13457  Utilities->CallLogPop(841);
13458  return(false);
13459  }
13460  }
13461  if(ForwardEntryPtr->Command == "Fjo")
13462  {
13463  if(ReverseEntryPtr->Command != "jbo")
13464  {
13465  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
13466  " and joins a train with headcode " + OtherHeadCode);
13467  TrainDataVector.clear();
13468  Utilities->CallLogPop(532);
13469  return(false);
13470  }
13471  else
13472  {
13473  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13474  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13475  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
13476  {
13477  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13478  }
13479  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
13480  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
13481  }
13482  }
13483  if(ForwardShuttleStart)
13484  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
13485  {
13486  if(!ReverseShuttleFinish)
13487  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
13488  {
13489  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
13490  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
13491  TrainDataVector.clear();
13492  Utilities->CallLogPop(1056);
13493  return(false);
13494  }
13495  }
13496  if(ReverseShuttleStart)
13497  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
13498  {
13499  if(!ForwardShuttleFinish)
13500  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
13501  {
13502  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
13503  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
13504  TrainDataVector.clear();
13505  Utilities->CallLogPop(1057);
13506  return(false);
13507  }
13508  else
13509  {
13510  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13511  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13512 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
13513  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
13514 */
13515  }
13516  }
13517  // check repeat information consistent if present
13518  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
13519  // and those not accessed here
13520 
13521  // still need to check the non-repeating links and that they have no repeats - do that outside this function
13522  bool MainRepeat = false, OtherRepeat = false;
13523  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
13524 
13525  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13526  {
13527  MainRepeat = true;
13528  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
13529  }
13530  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13531  {
13532  OtherRepeat = true;
13533  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
13534  }
13535  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
13536  {
13537  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
13538  " and the associated train with headcode " + OtherHeadCode);
13539  TrainDataVector.clear();
13540  Utilities->CallLogPop(844);
13541  return(false);
13542  }
13543  if(MainRepeat && OtherRepeat)
13544  {
13545  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
13546  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
13547  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
13548  {
13549  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
13550  " and the associated train with headcode " + OtherHeadCode);
13551  TrainDataVector.clear();
13552  Utilities->CallLogPop(845);
13553  return(false);
13554  }
13555  }
13556  Utilities->CallLogPop(863);
13557  return(true);
13558 }
13559 
13560 // ---------------------------------------------------------------------------
13561 
13562 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
13563 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
13564 {
13565  // strip spaces from extreme ends of input
13566  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
13567  if(Input == "")
13568  {
13569  Utilities->CallLogPop(856);
13570  return;
13571  }
13572  while(Input[1] == ' ')
13573  {
13574  if(Input.Length() > 1)
13575  {
13576  Input = Input.SubString(2, Input.Length() - 1);
13577  }
13578  else
13579  {
13580  Input = "";
13581  Utilities->CallLogPop(857);
13582  return;
13583  }
13584  }
13585  if(Input == "")
13586  {
13587  Utilities->CallLogPop(858);
13588  return;
13589  }
13590  while(Input[Input.Length()] == ' ')
13591  {
13592  if(Input.Length() > 1)
13593  {
13594  Input = Input.SubString(1, Input.Length() - 1);
13595  }
13596  else
13597  {
13598  Input = "";
13599  Utilities->CallLogPop(859);
13600  return;
13601  }
13602  }
13603  // now strip spaces immediately after all commas and semicolons within the text
13604  AnsiString Output = "";
13605  bool DelimiterFound = false;
13606 
13607  for(int x = 1; x < Input.Length() + 1; x++)
13608  {
13609  if(DelimiterFound)
13610  {
13611  if(Input[x] == ' ')
13612  {
13613  continue;
13614  }
13615  }
13616  if((Input[x] != ',') && (Input[x] != ';'))
13617  {
13618  DelimiterFound = false;
13619  Output = Output + Input[x];
13620  }
13621  else
13622  {
13623  DelimiterFound = true;
13624  Output = Output + Input[x];
13625  }
13626  }
13627  if(Output == "")
13628  {
13629  Input = "";
13630  Utilities->CallLogPop(860);
13631  return;
13632  }
13633  // now strip spaces immediately before all commas and semicolons within the text
13634  Input = Output;
13635  Output = "";
13636  DelimiterFound = false;
13637  for(int x = Input.Length(); x > 0; x--)
13638  {
13639  if(DelimiterFound)
13640  {
13641  if(Input[x] == ' ')
13642  {
13643  continue;
13644  }
13645  }
13646  if((Input[x] != ',') && (Input[x] != ';'))
13647  {
13648  DelimiterFound = false;
13649  Output = AnsiString(Input[x]) + Output;
13650  }
13651  else
13652  {
13653  DelimiterFound = true;
13654  Output = AnsiString(Input[x]) + Output;
13655  }
13656  }
13657  Input = Output;
13658  Utilities->CallLogPop(861);
13659 }
13660 
13661 // ---------------------------------------------------------------------------
13662 
13663 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
13664 // checks if an Snt or Snt-sh entry followed (somewhere, not necessarily immediately) by a TimeLoc has the same LocationName
13665 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
13666 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
13667 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
13668 // are done in this function, they must be done elsewhere.
13669 {
13670  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
13671  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13672 
13673  LocationName = "";
13674  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
13675  {
13676  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
13677  }
13679  {
13680  Utilities->CallLogPop(852);
13681  return(false);
13682  }
13683  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
13684  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
13685 
13686  if(LocRear != "")
13687  {
13688  LocationName = LocRear;
13689  }
13690  else
13691  {
13692  LocationName = LocFront;
13693  }
13694  if(LocationName == "")
13695  {
13696  Utilities->CallLogPop(1036);
13697  return(false);
13698  }
13699  if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed == 0))
13700  {
13701  Utilities->CallLogPop(1773);
13702  return(true);
13703  }
13704  else if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed > 0))
13705  {
13706  LocationName = "";
13707  Utilities->CallLogPop(1784);
13708  return(false);
13709  }
13710  // here if not a signaller start entry so must be at least one more entry
13711  const TActionVectorEntry &AVEntry1 = TDEntry.ActionVector.at(1);
13712 
13713  // has to be at least 2 AV entries to pass the > 1 comma test in the preliminary check
13714  if(((AVEntry1.Command == "Frh") || (AVEntry1.Command == "Fjo") || (AVEntry1.Command == "F-nshs") || (AVEntry1.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
13715  {
13716  //added F-nshs at v2.5.1 so can stay at a
13717  Utilities->CallLogPop(1037); //location until become a new shuttle service
13718  return(true); //added Fns at same time as saw no reason to exclude
13719  }
13720  AnsiString TimeLocLocationName;
13721  bool FoundFlag = false;
13722 
13723  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
13724  {
13725  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
13726  if(AVEntry.FormatType == TimeLoc)
13727  {
13728  FoundFlag = true;
13729  TimeLocLocationName = AVEntry.LocationName;
13730  break;
13731  }
13732  }
13733  if(!FoundFlag)
13734  {
13735  Utilities->CallLogPop(853);
13736  return(false);
13737  }
13738  if(TimeLocLocationName == LocationName)
13739  {
13740  Utilities->CallLogPop(854);
13741  return(true);
13742  }
13743  Utilities->CallLogPop(855);
13744  return(false);
13745 }
13746 
13747 // ---------------------------------------------------------------------------
13748 
13749 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
13750 {
13751  // checks that the new train start elements are valid - both exist & are connected, and that not
13752  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
13753  // & not starting with front on a continuation
13754  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
13755  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
13756 
13757  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
13758  if(RearPosition < 0)
13759  // error message given in GetTrackVectorPositionFromString
13760  {
13761  Utilities->CallLogPop(759);
13762  return(false);
13763  }
13764  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
13765  if(FrontPosition < 0)
13766  // error message given in GetTrackVectorPositionFromString
13767  {
13768  Utilities->CallLogPop(760);
13769  return(false);
13770  }
13771  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
13772  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
13773  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
13774 
13775  // check front & rear connected
13776  for(int x = 0; x < 4; x++)
13777  {
13778  if(RearTrackElement.Conn[x] == FrontPosition)
13779  {
13780  RearExitPos = x;
13781  break;
13782  }
13783  if(x == 3)
13784  {
13785  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
13786  Utilities->CallLogPop(762);
13787  return(false);
13788  }
13789  }
13790  // check not starting with front on a continuation
13791  if(FrontType == Continuation)
13792  {
13793  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
13794  Utilities->CallLogPop(937);
13795  return(false);
13796  }
13797  // check not starting on a level crossing
13798  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
13799  {
13800  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
13801  Utilities->CallLogPop(1951);
13802  return(false);
13803  }
13804  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
13805  {
13806  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
13807  Utilities->CallLogPop(1952);
13808  return(false);
13809  }
13810  // check if trying to start on diverging leg of points
13811  if((RearType == Points) && (RearExitPos == 3))
13812  {
13813  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
13814  Utilities->CallLogPop(936);
13815  return(false);
13816  }
13817  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
13818  {
13819  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
13820  Utilities->CallLogPop(1808);
13821  return(false);
13822  }
13823  Utilities->CallLogPop(905);
13824  return(true);
13825 }
13826 
13827 // ---------------------------------------------------------------------------
13828 
13829 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
13830 // Rear & front element validity already checked in CheckStartPositionValidity
13831 // This checks for points in correct orientation, no train at start position and not starting on a locked route
13832 {
13833  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
13834  AnsiString(RearExitPos));
13835  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
13836 
13837  if(RearTrackElement.TrackType == Continuation)
13838  {
13839  EventType = FailTrainEntry;
13840  }
13841  else
13842  {
13843  EventType = FailCreateTrain;
13844  }
13845  int FrontPosition = RearTrackElement.Conn[RearExitPos];
13846  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
13847  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
13848  TTrackType RearType = RearTrackElement.TrackType;
13849  TTrackType FrontType = FrontTrackElement.TrackType;
13850  AnsiString RearName, FrontName;
13851 
13852  if(RearTrackElement.ActiveTrackElementName != "")
13853  {
13854  RearName = RearTrackElement.ActiveTrackElementName;
13855  }
13856  else
13857  {
13858  RearName = RearTrackElement.ElementID;
13859  }
13860  if(FrontTrackElement.ActiveTrackElementName != "")
13861  {
13862  FrontName = FrontTrackElement.ActiveTrackElementName;
13863  }
13864  else
13865  {
13866  FrontName = FrontTrackElement.ElementID;
13867  }
13868  TPrefDirElement PrefDirElement; // needed for next function but not used
13869  int LockedVectorNumber; // needed for next function but not used
13870 
13871  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
13872  {
13873  if(ReportFlag)
13874  {
13875  if(EventType == FailCreateTrain)
13876  {
13877  EventType = FailCreateLockedRoute;
13878  }
13879  else
13880  {
13881  EventType = FailEnterLockedRoute;
13882  }
13883  LogActionError(47, HeadCode, "", EventType, FrontName);
13884  }
13885  Utilities->CallLogPop(940);
13886  return(false);
13887  }
13888  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
13889  {
13890  if(ReportFlag)
13891  {
13892  if(EventType == FailCreateTrain)
13893  {
13894  EventType = FailCreateLockedRoute;
13895  }
13896  else
13897  {
13898  EventType = FailEnterLockedRoute;
13899  }
13900  LogActionError(48, HeadCode, "", EventType, RearName);
13901  }
13902  Utilities->CallLogPop(1809);
13903  return(false);
13904  }
13905  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
13906  {
13907  if(ReportFlag)
13908  {
13909  LogActionError(27, HeadCode, "", EventType, RearName);
13910  }
13911  Utilities->CallLogPop(1810);
13912  return(false);
13913  }
13914  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
13915  {
13916  if(ReportFlag)
13917  {
13918  if(EventType == FailCreateTrain)
13919  {
13920  LogActionError(28, HeadCode, "", EventType, FrontName);
13921  }
13922  else
13923  {
13924  LogActionError(43, HeadCode, "", EventType, RearName);
13925  }
13926  }
13927  Utilities->CallLogPop(941);
13928  return(false);
13929  }
13930  if(RearType == Bridge)
13931  {
13932  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13933  {
13934  if(ReportFlag)
13935  {
13936  LogActionError(29, HeadCode, "", EventType, RearName);
13937  }
13938  Utilities->CallLogPop(942);
13939  return(false);
13940  }
13941  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13942  {
13943  if(ReportFlag)
13944  {
13945  LogActionError(30, HeadCode, "", EventType, RearName);
13946  }
13947  Utilities->CallLogPop(943);
13948  return(false);
13949  }
13950  }
13951  if(FrontType == Bridge)
13952  {
13953  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13954  {
13955  if(ReportFlag)
13956  {
13957  if(EventType == FailCreateTrain)
13958  {
13959  LogActionError(31, HeadCode, "", EventType, FrontName);
13960  }
13961  else
13962  {
13963  LogActionError(44, HeadCode, "", EventType, RearName);
13964  }
13965  }
13966  Utilities->CallLogPop(944);
13967  return(false);
13968  }
13969  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13970  {
13971  if(ReportFlag)
13972  {
13973  if(EventType == FailCreateTrain)
13974  {
13975  LogActionError(45, HeadCode, "", EventType, FrontName);
13976  }
13977  else
13978  {
13979  LogActionError(46, HeadCode, "", EventType, RearName);
13980  }
13981  }
13982  Utilities->CallLogPop(945);
13983  return(false);
13984  }
13985  }
13986  EventType = FailCreatePoints;
13987  if(RearType == Points)
13988  {
13989  if(RearTrackElement.Attribute == 1)
13990  {
13991  if(ReportFlag)
13992  {
13993  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
13994  }
13995  Utilities->CallLogPop(933);
13996  return(false);
13997  }
13998  }
13999  if(FrontType == Points)
14000  {
14001  if(FrontTrackElement.Attribute == 1)
14002  {
14003  if(ReportFlag)
14004  {
14005  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
14006  }
14007  Utilities->CallLogPop(934);
14008  return(false);
14009  }
14010  }
14011  Utilities->CallLogPop(939);
14012  return(true);
14013 }
14014 
14015 // ---------------------------------------------------------------------------
14016 
14017 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
14018 {
14019  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
14020  "," + AnsiString(IncDigits));
14021  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
14022  {
14023  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
14024  }
14025  if(!Last2CharactersBothDigits(2, BaseHeadCode))
14026  {
14027  Utilities->CallLogPop(1893);
14028  return(BaseHeadCode);
14029  }
14030  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
14031  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
14032 
14033  while(NextRepeatDigits >= 100)
14034  {
14035  NextRepeatDigits -= 100; // rolls over after 99
14036  }
14037  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
14038 
14039  if(NextRepeatDigitsStr.Length() < 2)
14040  {
14041  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
14042  }
14043  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
14044 
14045  Utilities->CallLogPop(1365);
14046  return(NextRepeatHeadCode);
14047 }
14048 
14049 // ---------------------------------------------------------------------------
14050 
14051 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
14052 {
14053  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
14054  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
14055  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
14056 
14057  Utilities->CallLogPop(1366);
14058  return(NextRepeatTime);
14059 }
14060 
14061 // ---------------------------------------------------------------------------
14062 
14063 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
14064 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
14065 {
14066  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
14067  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
14068  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14069  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14070  int RepeatSecs = RepeatMinutes * 60;
14071 
14072  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
14073  {
14074  Utilities->CallLogPop(1367);
14075  return(false);
14076  }
14077  else
14078  {
14079  Utilities->CallLogPop(1368);
14080  return(true);
14081  }
14082 }
14083 
14084 // ---------------------------------------------------------------------------
14085 
14086 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
14087 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14088 {
14089  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
14090  NonRepeatingHeadCode);
14091  int ForwardCount = 0;
14092  int ReverseCount = 0;
14093  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14094  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14095  // Forward corresponds to Main, Reverse to Other
14096  TTrainDataEntry *MainTrainDataPtr = 0;
14097  TTrainDataEntry *OtherTrainDataPtr = 0;
14098 
14099  // forward check
14100  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14101  {
14102  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14103  if(TDEntry.HeadCode == MainHeadCode)
14104  {
14105  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14106  {
14107  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14108  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
14109  {
14110  MainTrainDataPtr = &TrainDataVector.at(x);
14111  ForwardEntryPtr = &AVEntry;
14112  ForwardCount++;
14113  ForwardTDVectorNumber = x;
14114  }
14115  }
14116  }
14117  }
14118  if(ForwardCount == 0)
14119  // this is an exception because the headcodes are selected in the same order as the forward check
14120  {
14121  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
14122  }
14123  if(ForwardCount > 1)
14124  {
14125  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
14126  MainHeadCode);
14127  TrainDataVector.clear();
14128  Utilities->CallLogPop(1061);
14129  return(false);
14130  }
14131  // reverse check
14132  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14133  {
14134  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14135  if(TDEntry.HeadCode == NonRepeatingHeadCode)
14136  {
14137  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14138  {
14139  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14140  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14141  {
14142  OtherTrainDataPtr = &TrainDataVector.at(x);
14143  ReverseCount++;
14144  ReverseEntryPtr = &AVEntry;
14145  ReverseTDVectorNumber = x;
14146  }
14147  }
14148  }
14149  }
14150 
14151  if(ReverseCount == 0)
14152  {
14153  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
14154  TrainDataVector.clear();
14155  Utilities->CallLogPop(1062);
14156  return(false);
14157  }
14158  if(ReverseCount > 1)
14159  {
14160  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14161  NonRepeatingHeadCode);
14162  TrainDataVector.clear();
14163  Utilities->CallLogPop(1063);
14164  return(false);
14165  }
14166  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
14167  {
14168  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
14169  TrainDataVector.clear();
14170  Utilities->CallLogPop(1064);
14171  return(false);
14172  }
14173  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
14174  {
14175  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
14176  TrainDataVector.clear();
14177  Utilities->CallLogPop(1065);
14178  return(false);
14179  }
14180  if(ForwardEntryPtr->LocationName == "")
14181  {
14182  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14183  ". One or other service does not have a location set");
14184  TrainDataVector.clear();
14185  Utilities->CallLogPop(1066);
14186  return(false);
14187  }
14188  if(ReverseEntryPtr->LocationName == "")
14189  {
14190  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14191  ". One or other service does not have a location set");
14192  TrainDataVector.clear();
14193  Utilities->CallLogPop(1067);
14194  return(false);
14195  }
14196  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14197  {
14198  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
14199  " is at a different location to the referencing train " + MainHeadCode);
14200  TrainDataVector.clear();
14201  Utilities->CallLogPop(1068);
14202  return(false);
14203  }
14204  if(ForwardEntryPtr->Command == "F-nshs")
14205  // i.e. the non repeating link into the shuttle service
14206  {
14207  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14208  {
14209  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
14210  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
14211  TrainDataVector.clear();
14212  Utilities->CallLogPop(1069);
14213  return(false);
14214  }
14215  }
14216  if(ForwardEntryPtr->Command == "Fns-sh")
14217  // i.e. the non repeating link out from the shuttle service
14218  {
14219  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
14220  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
14221  {
14222  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
14223  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
14224  TrainDataVector.clear();
14225  Utilities->CallLogPop(1070);
14226  return(false);
14227  }
14228  }
14229  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
14230  // i.e. a non repeating link to or from the shuttle service
14231  {
14232  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14233  {
14234  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
14235  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
14236  TrainDataVector.clear();
14237  Utilities->CallLogPop(1071);
14238  return(false);
14239  }
14240  }
14241 /* it's allowed to have a different description
14242  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
14243  {
14244  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
14245  {
14246  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
14247  TrainDataVector.clear();
14248  Utilities->CallLogPop(1072);
14249  return false;
14250  }
14251  }
14252 */
14253  if(ForwardEntryPtr->Command == "Sns-sh")
14254  {
14255  if(ReverseEntryPtr->Command != "F-nshs")
14256  {
14257  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
14258  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
14259  TrainDataVector.clear();
14260  Utilities->CallLogPop(1073);
14261  return(false);
14262  }
14263  }
14264  if(ForwardEntryPtr->Command == "F-nshs")
14265  {
14266  if(ReverseEntryPtr->Command != "Sns-sh")
14267  {
14268  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
14269  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
14270  TrainDataVector.clear();
14271  Utilities->CallLogPop(1074);
14272  return(false);
14273  }
14274  else
14275  {
14276  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14277  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
14278  if(OtherTrainDataPtr->Description == "")
14279  {
14280  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14281  }
14282  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14283  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14284  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14285  }
14286  }
14287  if(ForwardEntryPtr->Command == "Sns-fsh")
14288  {
14289  if(ReverseEntryPtr->Command != "Fns-sh")
14290  {
14291  SecondPassMessage(GiveMessages,
14292  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
14293  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
14294  TrainDataVector.clear();
14295  Utilities->CallLogPop(1075);
14296  return(false);
14297  }
14298  }
14299  if(ForwardEntryPtr->Command == "Fns-sh")
14300  {
14301  if(ReverseEntryPtr->Command != "Sns-fsh")
14302  {
14303  SecondPassMessage(GiveMessages,
14304  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
14305  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
14306  TrainDataVector.clear();
14307  Utilities->CallLogPop(1076);
14308  return(false);
14309  }
14310  else
14311  {
14312  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
14313  // links to the non-repeating non-shuttle linked service
14314  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14315  // needed for creating formatted timetable
14316  if(OtherTrainDataPtr->Description == "")
14317  {
14318  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14319  }
14320  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14321  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14322  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14323  }
14324  }
14325  Utilities->CallLogPop(1077);
14326  return(true);
14327 }
14328 
14329 // ---------------------------------------------------------------------------
14330 
14331 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
14332 // Forward train is the finish shuttle entry 'Fns-sh'.
14333 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
14334 {
14335  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
14336  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
14337  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14338  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14339  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
14340 
14341  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
14342  {
14343  Utilities->CallLogPop(1369);
14344  return(false);
14345  }
14346  else
14347  {
14348  Utilities->CallLogPop(1370);
14349  return(true);
14350  }
14351 }
14352 
14353 // ---------------------------------------------------------------------------
14354 
14355 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
14356 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14357 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14358 // don't ever need to and as designed would skip repeats.
14359 
14360 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
14361 {
14362  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
14363  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
14364  {
14365  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
14366  }
14367  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
14368  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
14369  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14370 
14371  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14372  {
14373  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14374  TrainDataVector.clear();
14375  Utilities->CallLogPop(1091);
14376  return(false);
14377  }
14378  while(LastActionCommand == "Fns")
14379  {
14380  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
14381  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14382  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14383  {
14384  SecondPassMessage(GiveMessages,
14385  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
14386  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14387  TrainDataVector.clear();
14388  Utilities->CallLogPop(1092);
14389  return(false);
14390  }
14391  }
14392  // exit the 'while' with LastActionCommand FSH-XX
14393  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
14394  {
14395  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
14396  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
14397  ". The linking of two or more shuttles is not permitted.");
14398  TrainDataVector.clear();
14399  Utilities->CallLogPop(1093);
14400  return(false);
14401  }
14402  Utilities->CallLogPop(1094);
14403  return(true);
14404 }
14405 
14406 // ---------------------------------------------------------------------------
14407 
14408 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
14409 {
14410  if(!GiveMessages)
14411  {
14412  return;
14413  }
14414  // if(ServiceReference == "") ShowMessage(Message);
14415  if(!CheckHeadCodeValidity(12, false, ServiceReference))
14416  {
14417  ShowMessage(Message);
14418  }
14419  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
14420  // false means don't give messages within the function
14421  else
14422  {
14423  ShowMessage("Service " + ServiceReference + ": " + Message);
14424  }
14425 }
14426 
14427 // ---------------------------------------------------------------------------
14428 
14429 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
14430 {
14431  if(!GiveMessages)
14432  {
14433  return;
14434  }
14435  ShowMessage(Message);
14436 }
14437 
14438 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
14439 // ---------------------------------------------------------------------------
14440 
14441 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
14442 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
14443 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
14444 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
14445 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
14446 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
14447 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
14448 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
14449 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
14450 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
14451 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
14452 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
14453 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
14454 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
14455 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
14456 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
14457 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
14458 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
14459 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
14460 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
14461 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
14462 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
14463 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
14464 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
14465 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
14466 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
14467 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
14468 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
14469 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
14470 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
14471 {
14472  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
14473  AnsiString(ActionEventType) + "," + LocationID);
14474  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
14475 
14476  Prefix = " ERROR: ";
14477  if(ActionEventType == FailTrainEntry)
14478  {
14479  Prefix = " HELD: ";
14480  ErrorLog = " can't enter railway, train obstructing entry position ";
14481  WarningStr = " can't enter railway, train obstructing entry position ";
14482  Display->WarningLog(1, HeadCode + WarningStr + LocationID);
14483  }
14484  else if(ActionEventType == FailCreateTrain)
14485  {
14486  Prefix = " HELD: ";
14487  ErrorLog = " can't be created, train obstructing ";
14488  WarningStr = " can't be created, train obstructing ";
14489  Display->WarningLog(2, HeadCode + WarningStr + LocationID);
14490  }
14491  else if(ActionEventType == FailCreateLockedRoute)
14492  {
14493  Prefix = " HELD: ";
14494  ErrorLog = " can't be created on a locked route at ";
14495  WarningStr = " can't be created on a locked route at ";
14496  Display->WarningLog(4, HeadCode + WarningStr + LocationID);
14497  }
14498  else if(ActionEventType == FailEnterLockedRoute)
14499  {
14500  Prefix = " HELD: ";
14501  ErrorLog = " can't enter on a locked route at ";
14502  WarningStr = " can't enter on a locked route at ";
14503  Display->WarningLog(5, HeadCode + WarningStr + LocationID);
14504  }
14505  else if(ActionEventType == FailCreatePoints)
14506  {
14507  Prefix = " HELD: ";
14508  ErrorLog = " can't be created, diverging points at ";
14509  WarningStr = " can't be created, diverging points at ";
14510  Display->WarningLog(3, HeadCode + WarningStr + LocationID);
14511  }
14512  else if(ActionEventType == FailUnexpectedExitRailway)
14513  {
14514  ErrorLog = " left railway unexpectedly at ";
14515  UnexpectedExits++;
14516  }
14517  else if(ActionEventType == FailIncorrectExit)
14518  {
14519  ErrorLog = " left railway at an incorrect exit at ";
14520  IncorrectExits++;
14521  }
14522  else if(ActionEventType == FailLocTooShort)
14523  {
14524  ErrorLog = " failed to split - location too short at ";
14525  WarningStr = " failed to split, location too short at ";
14526  Display->WarningLog(6, HeadCode + WarningStr + LocationID);
14527  }
14528  else if(ActionEventType == FailSplitDueToOtherTrain)
14529  {
14530  Prefix = " HELD: ";
14531  ErrorLog = " unable to split - other train obstructing at ";
14532  WarningStr = " unable to split - other train obstructing at ";
14533  Display->WarningLog(7, HeadCode + WarningStr + LocationID);
14534  }
14535  else if(ActionEventType == FailUnexpectedBuffers)
14536  {
14537  ErrorLog = " stopped at buffers unexpectedly at position ";
14538  }
14539  else if(ActionEventType == FailMissedArrival)
14540  {
14541  ErrorLog = " failed to stop at ";
14542  MissedStops++;
14543  }
14544  else if(ActionEventType == FailMissedSplit)
14545  {
14546  ErrorLog = " failed to split at ";
14548  }
14549  else if(ActionEventType == FailMissedJBO)
14550  {
14551  ErrorLog = " failed to be joined by other train at ";
14553  }
14554  else if(ActionEventType == FailMissedJoinOther)
14555  {
14556  ErrorLog = " failed to join other train at ";
14558  }
14559  else if(ActionEventType == FailMissedTerminate)
14560  {
14561  ErrorLog = " failed to terminate at ";
14563  }
14564  else if(ActionEventType == FailMissedNewService)
14565  {
14566  ErrorLog = " failed to form new service at ";
14568  }
14569  else if(ActionEventType == FailMissedExitRailway)
14570  {
14571  ErrorLog = " failed to exit railway ";
14573  }
14574  else if(ActionEventType == FailMissedChangeDirection)
14575  {
14576  ErrorLog = " failed to change direction at ";
14578  }
14579  else if(ActionEventType == FailMissedPass)
14580  {
14581  ErrorLog = " failed to pass ";
14583  }
14584  else if(ActionEventType == FailBuffersPreventingStart)
14585  {
14586  ErrorLog = " facing buffers and unable to start at ";
14587  }
14588  else if(ActionEventType == FailDerailed)
14589  {
14590  ErrorLog = " DERAILED at position ";
14591  Prefix = " DERAILMENT: ";
14592  Derailments++;
14593  }
14594  else if(ActionEventType == FailBufferCrash)
14595  {
14596  ErrorLog = " CRASHED INTO BUFFERS at ";
14597  Prefix = " CRASH: ";
14598  CrashedTrains++;
14599  }
14600  else if(ActionEventType == FailLevelCrossingCrash)
14601  {
14602  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
14603  Prefix = " CRASH: ";
14604  CrashedTrains++;
14605  }
14606  else if(ActionEventType == FailCrashed)
14607  {
14608  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
14609  Prefix = " CRASH: ";
14610  CrashedTrains++;
14611  CrashedTrains++;
14612  }
14613  else if(ActionEventType == FailSPAD)
14614  {
14615  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
14616  Prefix = " SPAD: ";
14617  SPADEvents++;
14618  }
14619  else if(ActionEventType == FailLockedRoute)
14620  {
14621  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
14622  Prefix = " SPAD RISK: ";
14623  SPADRisks++;
14624  }
14625  else if(ActionEventType == RouteForceCancelled)
14626  {
14627  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
14628  }
14629  else if(ActionEventType == WaitingForJBO)
14630  {
14631  Prefix = " WARNING: ";
14632  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
14633  WarningStr = " waiting to join " + OtherHeadCode + " at ";
14634  Display->WarningLog(8, HeadCode + WarningStr + LocationID);
14635  }
14636  else if(ActionEventType == WaitingForFJO)
14637  {
14638  Prefix = " WARNING: ";
14639  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
14640  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
14641  Display->WarningLog(9, HeadCode + WarningStr + LocationID);
14642  }
14643  TDateTime ActualTime = TrainController->TTClockTime;
14644 
14645  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
14646  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
14647  Utilities->CallLogPop(1371);
14648 }
14649 
14650 // ---------------------------------------------------------------------------
14651 
14653 {
14654 /*
14655  TrainDataEntry
14656  AnsiString HeadCode, Description;//null on creation
14657  int StartSpeed, MaxRunningSpeed;//both kph
14658  int RepeatNumber;
14659  TActionVector ActionVector;
14660  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
14661  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
14662 
14663  ActionVectorEntry
14664  TTimetableEntryType FormatType;
14665  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
14666  AnsiString LocationName, Command, OtherHeadCode;//null on creation
14667  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
14668  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
14669  int RepeatNumber;
14670 
14671  TrainOperatingData
14672  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
14673  int TrainID;
14674  TRunningEntry RunningEntry;
14675  TDateTime StartTime;
14676  AnsiString HeadCode;
14677 */
14678  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
14679  std::ofstream OutFile("TrainData.csv");
14680 
14681  if(OutFile == 0)
14682  {
14683  ShowMessage("Output file TrainData.csv failed to open");
14684  Utilities->CallLogPop(1372);
14685  return;
14686  }
14687  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14688  {
14689  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14690  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
14691 
14692  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
14693  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
14694 
14695  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
14696  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
14697  "RepeatNumber" << '\n' << '\n';
14698  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14699  {
14700  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14701  AnsiString TimetableEntryTypeStr;
14702  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
14703  switch(AVEntry.FormatType)
14704  {
14705  case 0:
14706  {
14707  TimetableEntryTypeStr = "NoFormat";
14708  break;
14709  }
14710 
14711  case 1:
14712  {
14713  TimetableEntryTypeStr = "TimeLoc";
14714  break;
14715  }
14716 
14717  case 2:
14718  {
14719  TimetableEntryTypeStr = "TimeTimeLoc";
14720  break;
14721  }
14722 
14723  case 3:
14724  {
14725  TimetableEntryTypeStr = "TimeCmd";
14726  break;
14727  }
14728 
14729  case 4:
14730  {
14731  TimetableEntryTypeStr = "StartNew";
14732  break;
14733  }
14734 
14735  case 5:
14736  {
14737  TimetableEntryTypeStr = "TimeCmdHeadCode";
14738  break;
14739  }
14740 
14741  case 6:
14742  {
14743  TimetableEntryTypeStr = "FinRemHere";
14744  break;
14745  }
14746 
14747  case 7:
14748  {
14749  TimetableEntryTypeStr = "FNSShuttle";
14750  break;
14751  }
14752 
14753  case 8:
14754  {
14755  TimetableEntryTypeStr = "SNTShuttle";
14756  break;
14757  }
14758 
14759  case 9:
14760  {
14761  TimetableEntryTypeStr = "SNSShuttle";
14762  break;
14763  }
14764 
14765  case 10:
14766  {
14767  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
14768  break;
14769  }
14770 
14771  case 11:
14772  {
14773  TimetableEntryTypeStr = "FSHNewService";
14774  break;
14775  }
14776 
14777  case 12:
14778  {
14779  TimetableEntryTypeStr = "Repeat";
14780  break;
14781  }
14782 
14783  default:
14784  {
14785  TimetableEntryTypeStr = "Default";
14786  break;
14787  }
14788  }
14789  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
14790  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
14791  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
14792  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
14793  AVEntry.NumberOfRepeats << '\n';
14794  }
14795  OutFile << '\n';
14796  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
14797  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
14798  {
14799  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
14800  AnsiString RunningEntryStr;
14801  // NotStarted, Running, Exited
14802  switch(TOD.RunningEntry)
14803  {
14804  case 0:
14805  {
14806  RunningEntryStr = "NotStarted";
14807  break;
14808  }
14809 
14810  case 1:
14811  {
14812  RunningEntryStr = "Running";
14813  break;
14814  }
14815 
14816  case 2:
14817  {
14818  RunningEntryStr = "Exited";
14819  break;
14820  }
14821  }
14822  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
14823  }
14824  OutFile << '\n';
14825  }
14826  OutFile.close();
14827  Utilities->CallLogPop(1373);
14828 }
14829 
14830 // ---------------------------------------------------------------------------
14831 
14832 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
14833 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
14834 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
14835 {
14836  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
14837  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14839  ShowMessage(Message);
14840  BaseTime = TDateTime::CurrentDateTime();
14841  StopTTClockFlag = false;
14842  Utilities->CallLogPop(1374);
14843 }
14844 
14845 // ---------------------------------------------------------------------------
14846 
14847 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
14848 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
14849 // from the start of the relevant vectors. Can't save the pointer values
14850 // as these will be different each time the vectors are created
14851 {
14852  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
14853  Utilities->SaveFileInt(SessionFile, TrainVector.size());
14854  for(unsigned int x = 0; x < TrainVector.size(); x++)
14855  {
14856  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
14857  }
14858  Utilities->CallLogPop(1375);
14859 }
14860 
14861 // ---------------------------------------------------------------------------
14862 
14863 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
14864 {
14865  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
14866  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
14867  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14868  // by zero error in calculating AValue, use 1
14869  for(int x = 0; x < NumberOfTrains; x++)
14870  {
14871  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14872  // by zero error in calculating AValue, use 1
14873  NewTrain->LoadOneSessionTrain(0, SessionFile);
14874  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
14875  // added at v2.4.0. have to include as that value not stored in session file
14876  {
14877  NewTrain->StoppedWithoutPower = true;
14878  }
14879  TrainVector.push_back(*NewTrain);
14880  LastTrainLoaded = x;
14881  }
14882  delete NewTrain;
14883  Utilities->CallLogPop(1376);
14884 }
14885 
14886 // ---------------------------------------------------------------------------
14887 
14888 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
14889 {
14890  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
14891  int NumberOfTrains;
14892 
14893  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
14894  {
14895  Utilities->CallLogPop(1377);
14896  return(false);
14897  }
14898  for(int x = 0; x < NumberOfTrains; x++)
14899  {
14900  if(!(TTrain::CheckOneSessionTrain(InFile)))
14901  {
14902  Utilities->CallLogPop(1378);
14903  return(false);
14904  }
14905  }
14906  Utilities->CallLogPop(1379);
14907  return(true);
14908 }
14909 
14910 // ---------------------------------------------------------------------------
14911 
14912 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
14913 {
14914  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
14915  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
14916  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
14917  {
14918  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
14919  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
14920  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
14921  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
14922  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
14923  }
14924  Utilities->CallLogPop(1380);
14925 }
14926 
14927 // ---------------------------------------------------------------------------
14928 
14929 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14930 {
14931  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
14932  TAllRoutes::TLockedRouteClass LockedRouteObject;
14933  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
14934 
14935  for(int x = 0; x < LockedRouteVectorSize; x++)
14936  {
14937  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
14938  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14939  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14940  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
14941  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
14942  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
14943  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
14944  }
14945  Utilities->CallLogPop(1381);
14946 }
14947 
14948 // ---------------------------------------------------------------------------
14949 
14950 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14951 {
14952  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
14953  int LockedRouteVectorSize;
14954 
14955  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
14956  {
14957  Utilities->CallLogPop(1382);
14958  return(false);
14959  }
14960  for(int x = 0; x < LockedRouteVectorSize; x++)
14961  {
14962  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14963  {
14964  Utilities->CallLogPop(1383);
14965  return(false);
14966  }
14967  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14968  {
14969  Utilities->CallLogPop(1384);
14970  return(false);
14971  }
14972  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14973  {
14974  Utilities->CallLogPop(1385);
14975  return(false);
14976  }
14977  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
14978  {
14979  Utilities->CallLogPop(1386);
14980  return(false);
14981  }
14982  if(!Utilities->CheckFileDouble(SessionFile))
14983  {
14984  Utilities->CallLogPop(1387);
14985  return(false);
14986  }
14987  }
14988  Utilities->CallLogPop(1388);
14989  return(true);
14990 }
14991 
14992 // ---------------------------------------------------------------------------
14993 
14994 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
14995 {
14996  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
14997  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
14998  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
14999  {
15000  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
15001  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
15002  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
15003  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
15004  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
15005  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
15006  }
15007  Utilities->CallLogPop(1389);
15008 }
15009 
15010 // ---------------------------------------------------------------------------
15011 
15012 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15013 {
15014  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
15015  TContinuationAutoSigEntry ContinuationAutoSigObject;
15016  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
15017 
15018  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15019  {
15020  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15021  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
15022  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
15023  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
15024  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
15025  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
15026  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
15027  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
15028  }
15029  Utilities->CallLogPop(1390);
15030 }
15031 
15032 // ---------------------------------------------------------------------------
15033 
15034 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15035 {
15036  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
15037  int ContinuationAutoSigVectorSize;
15038 
15039  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
15040  {
15041  Utilities->CallLogPop(1391);
15042  return(false);
15043  }
15044  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15045  {
15046  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15047  {
15048  Utilities->CallLogPop(1392);
15049  return(false);
15050  }
15051  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15052  {
15053  Utilities->CallLogPop(1393);
15054  return(false);
15055  }
15056  if(!Utilities->CheckFileDouble(SessionFile))
15057  {
15058  Utilities->CallLogPop(1405);
15059  return(false);
15060  }
15061  if(!Utilities->CheckFileDouble(SessionFile))
15062  {
15063  Utilities->CallLogPop(1406);
15064  return(false);
15065  }
15066  if(!Utilities->CheckFileDouble(SessionFile))
15067  {
15068  Utilities->CallLogPop(1407);
15069  return(false);
15070  }
15071  if(!Utilities->CheckFileDouble(SessionFile))
15072  {
15073  Utilities->CallLogPop(1394);
15074  return(false);
15075  }
15076  }
15077  Utilities->CallLogPop(1395);
15078  return(true);
15079 }
15080 
15081 // ---------------------------------------------------------------------------
15082 
15083 /*
15084  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
15085  {
15086  public:
15087  AnsiString Description; ///< service description
15088  AnsiString HeadCode; ///< service headcode
15089  int RepeatNumber; ///< service RepeatNumber
15090  int IncrementalMinutes; ///< Repeat separation in minutes
15091  int IncrementalDigits; ///< Repeat headcode separation
15092  int VectorPosition; ///< TrackVectorPosition for the continuation element
15093  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
15094  };
15095 
15096 
15097  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
15098  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
15099 */
15100 
15102 // build this into timetable load so session loading can use it too
15103 // being a multimap it automatically sorts in ascending EventTime order
15104 {
15105  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
15107  // need to clear as this called twice when load a session
15108  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15109  {
15110  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
15111  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
15112  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
15113 
15114  if(AVFirstEntry.Command == "Snt")
15115  // new train (no need to include Snt-sh since they can't start at a continuation)
15116  {
15119  {
15121  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
15122  // retains this value for all repeats
15123  CTEEntry.RepeatNumber = 0; // for first entry
15124  CTEEntry.TrainDataEntryPtr = &TDEntry;
15125  // retains this value for all repeats
15126  CTEEntry.HeadCode = TDEntry.HeadCode;
15127  CTEEntry.Description = TDEntry.Description;
15128  CTEEntry.IncrementalMinutes = 0;
15129  CTEEntry.IncrementalDigits = 0;
15130  if(AVLastEntry.FormatType == Repeat)
15131  {
15132  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
15133  // retains this value or 0 for all repeats
15134  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
15135  // retains this value or 0 for all repeats
15136  }
15137  CTEMMP.first = AVFirstEntry.EventTime;
15138  CTEMMP.second = CTEEntry;
15139  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15140  // base entry
15141  if(TDEntry.NumberOfTrains > 1)
15142  {
15143  if(AVLastEntry.FormatType != Repeat)
15144  {
15145  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
15146  }
15147  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
15148  {
15149  CTEEntry.RepeatNumber = y;
15150  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
15151  // CTEEntry.VectorPosition stays same
15152  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
15153  CTEMMP.second = CTEEntry;
15154  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15155  }
15156  }
15157  }
15158  }
15159  }
15160  Utilities->CallLogPop(1396);
15161 }
15162 
15163 // ---------------------------------------------------------------------------
15164 
15166 {
15167  // called when WarningFlashCount == 0 or when press zoomout button
15168  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
15169  if(!Display->ZoomOutFlag)
15170  {
15171  Utilities->CallLogPop(1156);
15172  return;
15173  }
15174  for(unsigned int x = 0; x < TrainVector.size(); x++)
15175  {
15176  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
15177  // if OldPlotElement[x] == -1 then ignore (not plotted)
15179  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
15180  }
15181  Display->Update();
15182  // need to keep this since Update() not called for PlotSmallOutput as too slow
15183  Utilities->CallLogPop(742);
15184 }
15185 
15186 // ---------------------------------------------------------------------------
15187 
15188 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
15189 {
15190  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
15191  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
15192  {
15193  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
15194  }
15195  Utilities->CallLogPop(740);
15196  return(TrainVector.at(VecPos));
15197 }
15198 
15199 // ---------------------------------------------------------------------------
15200 
15201 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
15202 {
15203  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
15204  AnsiString RetStr = "", PartStr = "";
15205 
15206 
15207 /*
15208  Have description & mass etc for train at top - header, then array of actions
15209 
15210  class TActionVectorEntry
15211  {
15212  public:
15213  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
15215  bool SignallerControl;
15217  bool Warning;
15219  int NumberOfRepeats;
15221  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15223  TDateTime EventTime, ArrivalTime, DepartureTime;
15225  TExitList ExitList;
15227  TTimetableFormatType FormatType;
15229  TTimetableLocationType LocationType;
15231  TTimetableSequenceType SequenceType;
15233  TTimetableShuttleLinkType ShuttleLinkType;
15235  TTrainDataEntry *LinkedTrainEntryPtr;
15237  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
15239 
15240  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
15241 
15242  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
15243 
15244  class TTrainOperatingData
15245  {
15246  public:
15247  int TrainID;
15248  TActionEventType EventReported;
15249  TRunningEntry RunningEntry;
15250 
15251  //inline function
15252  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
15253  };
15254 
15255  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
15256 
15257  class TTrainDataEntry
15258  {
15259  public:
15260  AnsiString HeadCode, ServiceReference, Description;
15262  double MaxBrakeRate;
15264  double MaxRunningSpeed;
15266  double PowerAtRail;
15268  int Mass;
15270  int NumberOfTrains;
15272  int SignallerSpeed;
15274  int StartSpeed;
15276  TActionVector ActionVector;
15278  TTrainOperatingDataVector TrainOperatingDataVector;
15280 
15281  //inline function
15282  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
15283  };
15284 
15285  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
15286 
15287  //formatted timetable types
15288  class TOneTrainFormattedEntry
15289  {
15290  AnsiString Action;//includes location if relevanr
15291  AnsiString Time;
15292  };
15293 
15294  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15295 
15296  class TOneCompleteFormattedTrain//headcode + list of actions
15297  {
15298  public:
15299  AnsiString HeadCode;
15300  TOneFormattedTrainVector OneFormattedTrainVector;
15301  };
15302 
15303  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15304 
15305  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15306  {
15307  public:
15308  AnsiString Header;//description, mass, power, brake rate etc
15309  int NumberOfTrains;// number of repeats + 1
15310  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15311  };
15312 
15313 
15314  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15315  //end of formatted timetable types
15316 
15317 */
15318 
15319  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15320 
15321  // format "16/06/2009 20:55:17"
15322  // avoid characters in filename:= / \ : * ? " < > |
15323  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15324 
15325  AnsiString ShortTTName = "";
15326 
15327  for(int x = TTFileName.Length(); x > 0; x--)
15328  {
15329  if(TTFileName[x] == '\\')
15330  {
15331  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
15332  break;
15333  }
15334  }
15335 
15336  ShowMessage("Creates two timetables named " + ShortTTName +
15337  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
15338 
15339  Screen->Cursor = TCursor(-11); // Hourglass
15340 
15341  AnsiString FormatNoDPStr = "#######0";
15342  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
15343 
15345  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
15346  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
15347 
15348  // all timetable in formatted form
15349  //create the AllTTTrains vector
15350  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15351  {
15352  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
15353  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
15354  if(TrainDataEntry.Mass > 0)
15355  {
15356  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
15357  }
15358  if(TrainDataEntry.PowerAtRail > 0)
15359  {
15360  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
15361  }
15362  if(TrainDataEntry.MaxBrakeRate > 0)
15363  {
15364  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
15365  }
15366  if(TrainDataEntry.MaxRunningSpeed > 0)
15367  {
15368  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
15369  }
15370  FirstHeadCode = TrainDataEntry.HeadCode;
15371  int IncDigits = 0, IncMinutes = 0;
15372  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15373  if(!ActionVector.empty())
15374  {
15375  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15376  {
15377  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
15378  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15379  }
15380  }
15381  TTrainFormattedInformation OneTTLine;
15382  // contains all information for a single TT entry (including repeats)
15383  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
15384  {
15385  OneTTLine.Header = "";
15386  if((TrainDataEntry.Description != "") && (MassStr != ""))
15387  {
15388  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
15389  }
15390  else if(TrainDataEntry.Description != "")
15391  {
15392  OneTTLine.Header = TrainDataEntry.Description;
15393  }
15394  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
15395  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
15396  for(unsigned int z = 0; z < ActionVector.size(); z++)
15397  {
15398  TOneTrainFormattedEntry OneTTEntry;
15399  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
15400  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
15401  AnsiString PartStr = "", TimeStr = "";
15402 /*
15403  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
15404  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
15405  ExitRailway};
15406  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
15407  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
15408  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
15409 */
15410  if(ActionVectorEntry.SequenceType == Start)
15411  {
15412  if(ActionVectorEntry.FormatType == StartNew)
15413  {
15414  if(ActionVectorEntry.LocationName != "")
15415  {
15416  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15417  {
15418  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15419  }
15420  else
15421  {
15422  PartStr = "Created at " + ActionVectorEntry.LocationName;
15423  }
15424  }
15425  else // may be a named continuation or other element, and if so report that
15426  {
15427  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
15428  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15429  {
15430  if(LocName != "")
15431  {
15432  PartStr = "Enters at " + LocName;
15433  }
15434  else // use rear position if it's a continuation
15435  {
15436  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15437  }
15438  }
15439  else // not a continuation
15440  {
15441  if(LocName != "")
15442  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
15443  // but include anyway
15444  {
15445  PartStr = "Created at " + LocName;
15446  }
15447  else // use rear position again
15448  {
15449  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15450  }
15451  }
15452  }
15453  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
15454  }
15455  else if(ActionVectorEntry.FormatType == SNTShuttle)
15456  {
15457  if(y == 0) // first train
15458  {
15459  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15460  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
15461  }
15462  else
15463  {
15464  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15465  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15466  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
15467  } // y-1 for headcode above since it is the last repeat value that the train is from
15468 
15469  }
15470  else if(ActionVectorEntry.Command == "Sfs")
15471  {
15472  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
15473  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15474  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
15475  }
15476  else if(ActionVectorEntry.Command == "Sns")
15477  {
15478  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15479  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15480  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
15481  }
15482  else if(ActionVectorEntry.FormatType == SNSShuttle)
15483  {
15484  if(y == 0) // first entry from shuttle
15485  {
15486  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15487  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
15488  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
15489  }
15490  else
15491  {
15492  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15493  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15494  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
15495  } // y-1 for headcode above since it is the last repeat value that the train is from
15496 
15497  }
15498  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
15499  {
15500  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15501  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
15502  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
15503  AnsiString FirstHeadCode = TDE->HeadCode;
15504  int LastRepeatNumber = TDE->NumberOfTrains - 1;
15505  // a shuttle has to have at least 1 repeat
15506  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
15507  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
15508  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
15509  }
15510  }
15511  else if(ActionVectorEntry.SequenceType == Intermediate)
15512  {
15513  if(ActionVectorEntry.FormatType == TimeTimeLoc)
15514  {
15515  // here need 2 entries if times different so push the first right away & the second later
15516  // if times same just give the arrival entry
15517  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
15518  {
15519  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15520  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15521  OneTTEntry.Action = PartStr;
15522  OneTTEntry.Time = TimeStr;
15523  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15524  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15525  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
15526  }
15527  else
15528  {
15529  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
15530  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15531  }
15532  }
15533  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
15534  {
15535  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15536  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15537  }
15538  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
15539  {
15540  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15541  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
15542  }
15543  else if(ActionVectorEntry.FormatType == PassTime)
15544  {
15545  PartStr = "Passes " + ActionVectorEntry.LocationName;
15546  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
15547  }
15548  else if(ActionVectorEntry.Command == "jbo")
15549  {
15550  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
15551  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15552  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
15553  }
15554  else if(ActionVectorEntry.Command == "fsp")
15555  {
15556  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
15557  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15558  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
15559  }
15560  else if(ActionVectorEntry.Command == "rsp")
15561  {
15562  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
15563  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15564  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
15565  }
15566  else if(ActionVectorEntry.Command == "cdt")
15567  {
15568  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
15569  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
15570  }
15571  }
15572  else if(ActionVectorEntry.SequenceType == Finish)
15573  {
15574  if(ActionVectorEntry.Command == "Fns")
15575  {
15576  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15577  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15578  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
15579  }
15580  else if(ActionVectorEntry.Command == "F-nshs")
15581  {
15582  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15583  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15584  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
15585  }
15586  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15587  {
15588  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
15589  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15590  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
15591  // y+1 because it's the NEXT service repeat number that is relevant
15592  }
15593  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15594  {
15595  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15596  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15597  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
15598  }
15599  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15600  {
15601  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15602  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15603  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
15604  // y+1 because it's the NEXT service repeat number that is relevant
15605  }
15606  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15607  {
15608  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
15609  // only used in chronological tt
15610  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
15611  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
15612  }
15613  else if(ActionVectorEntry.Command == "Frh")
15614  {
15615  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
15616  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
15617  if(z > 0)
15618  // should be for finish entry but include check for safety
15619  {
15620  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
15621  {
15622  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
15623  }
15624  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
15625  {
15626  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
15627  }
15628  else
15629  {
15630  TimeStr = " "; // shouldn't ever get here
15631  }
15632  }
15633  }
15634  else if(ActionVectorEntry.Command == "Fer")
15635  {
15636  AnsiString AllowedExits;
15637  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
15638  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
15639  }
15640  else if(ActionVectorEntry.Command == "Fjo")
15641  {
15642  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
15643  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15644  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
15645  }
15646  }
15647  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
15648  {
15649  continue; // no entry needed for a repeat
15650  }
15651  OneTTEntry.Action = PartStr;
15652  OneTTEntry.Time = TimeStr;
15653  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15654  // one per action
15655  }
15656  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
15657  // one per repeat
15658  }
15659  AllTTTrains->push_back(OneTTLine); // one per repeating train
15660  }
15661  // AllTTTrains vector now complete
15662 
15663  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
15664 
15665  if(TTFile == 0)
15666  {
15667  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
15668  delete AllTTTrains;
15669  Utilities->CallLogPop(1567);
15670  return;
15671  }
15672 /* formatted timetable types
15673  class TOneTrainFormattedEntry
15674  {
15675  AnsiString Action;//includes location if relevant
15676  AnsiString Time;
15677  };
15678 
15679  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15680 
15681  class TOneCompleteFormattedTrain//headcode + list of actions
15682  {
15683  public:
15684  AnsiString HeadCode;
15685  TOneFormattedTrainVector OneFormattedTrainVector;
15686  };
15687 
15688  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15689 
15690  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15691  {
15692  public:
15693  AnsiString Header;//description, mass, power, brake rate etc
15694  int NumberOfTrains;// number of repeats + 1
15695  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15696  };
15697 
15698  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15699  //end of formatted timetable types
15700 */
15701 
15702  // new layout using multiple rows
15703  TTFile << TableTitle.c_str() << '\n' << '\n';
15704  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
15705  {
15706  TTFile << AllTTTrains->at(x).Header.c_str();
15707  TTFile << '\n';
15708  TTFile << ','; // for the blank line above the action list
15709  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15710  {
15711  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
15712  {
15713  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
15714  }
15715  else
15716  {
15717  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
15718  }
15719  }
15720  TTFile << '\n' << '\n';
15721 
15722  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
15723  {
15724  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
15725  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15726  {
15727  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
15728  {
15729  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
15730  }
15731  else
15732  {
15733  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
15734  }
15735  }
15736  TTFile << '\n';
15737  }
15738  TTFile << '\n' << '\n';
15739  }
15740 
15741  TTFile.close();
15742 
15743  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15744 
15745  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
15746 
15747  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
15748 
15749  if(TTFile2 == 0)
15750  {
15751  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
15752  delete AllTTTrains;
15753  Utilities->CallLogPop(1710);
15754  return;
15755  }
15756  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
15757  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
15758  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
15759 
15760  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
15761  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
15762 
15763  // multimap of AnsiStrings with TimeString as key (to sort automatically)
15764 
15765  TTFile2 << TableTitle.c_str() << '\n' << '\n';
15766  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
15767  {
15768  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15769  {
15770  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
15771  {
15772  bool GiveMessagesFalse = false;
15773  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
15774  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
15775  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
15776  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
15777  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
15778  {
15779  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
15780  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
15781  TimeString = TimeString.SubString(9, 5);
15782  ActionString += " " + OtherHeadCode;
15783  }
15784  if(TimeString.SubString(1, 7) == "End at ")
15785  // for Frh-sh final entry
15786  {
15787  TimeString = TimeString.SubString(8, 5);
15788  }
15789  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
15790  AnsiMultiMapEntry.first = TimeString;
15791  AnsiMultiMapEntry.second = OneLine;
15792  TAMM->insert(AnsiMultiMapEntry);
15793  }
15794  }
15795  }
15796 
15797  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
15798  {
15799  TTFile2 << (AMMIT->second).c_str();
15800  }
15801  delete AllTTTrains;
15802  delete TAMM;
15803  TTFile2.close();
15804  Utilities->CallLogPop(1580);
15805 }
15806 
15807 // ---------------------------------------------------------------------------
15808 
15809 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
15810  bool AtLocChecked, int ArrRange, int DepRange)
15811 {
15812 
15813  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
15814  bool AnalysisError = false;
15815 
15816  try
15817  {
15818  //New section after v2.4.3 for tt conflict analysis
15819  /*
15820  typedef std::list<AnsiString> TServiceCallingLocsList;
15821  typedef std::vector<TServiceCallingLocsList> TAllServiceCallingLocsMap;
15822 
15823  struct TLocServiceTimes
15824  {
15825  AnsiString Location;
15826  AnsiString ServiceAndRepeatNum;
15827  AnsiString AtLocTime;
15828  AnsiString ArrTime;
15829  AnsiString DepTime;
15830  };
15831  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
15832  */
15833 
15834  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
15835  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
15836 
15837  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
15838  TTrainDataVector::iterator TDVIt, TDVCopyIt;
15839  int Suffix = 0;
15840  int IteratorNumber = 0;
15841  AnsiString AnsiSuffix = "";
15842  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
15843  {
15844  IteratorNumber++; //first value in loop is 1
15845  Suffix = 0;
15846  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
15847  {
15848  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
15849  {
15850  Suffix++; //first value is 1
15851  AnsiSuffix = AnsiString(Suffix);
15852  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
15853  }
15854  }
15855  }
15856 
15857  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
15858  TServiceCallingLocsList ServiceCallingLocsList;
15859  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
15860  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15861  {
15862  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15863  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15864  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
15865  ServiceCallingLocsList.clear();
15866  if(ActionVector.empty())
15867  {
15868  continue;
15869  }
15870  if(ActionVector.at(0).SignallerControl)
15871  {
15872  continue;
15873  }
15874  for(unsigned int z = 0; z < ActionVector.size(); z++)
15875  {
15876  TActionVectorEntry AVE = ActionVector.at(z);
15877  if(AVE.FormatType == StartNew)
15878  {
15879  if(AVE.LocationType == AtLocation) //located Snt
15880  {
15881  ServiceCallingLocsList.push_back(AVE.LocationName);
15882  }
15883  else //unlocated Snt (could be entering at continuation)
15884  {
15886  if(TE.ActiveTrackElementName != "")
15887  {
15888  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
15889  }
15890  else
15891  {
15892  int HLoc = TE.HLoc;
15893  int VLoc = TE.VLoc;
15894  AnsiString HString;
15895  AnsiString VString;
15896  if(HLoc < 0)
15897  {
15898  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15899  }
15900  else
15901  {
15902  HString = AnsiString(HLoc);
15903  }
15904  if(VLoc < 0)
15905  {
15906  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15907  }
15908  else
15909  {
15910  VString = AnsiString(VLoc);
15911  }
15912  ServiceCallingLocsList.push_back(HString + '-' + VString);
15913  }
15914  }
15915  }
15916  else if(AVE.SequenceType == Start) //other start entries, all located
15917  {
15918  ServiceCallingLocsList.push_back(AVE.LocationName);
15919  }
15920  else if(AVE.FormatType == TimeLoc) //z must be > 0
15921  {
15922  if(ServiceCallingLocsList.back() != AVE.LocationName)
15923  {
15924  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
15925  }
15926  }
15927  else if(AVE.FormatType == PassTime)
15928  {
15929  ServiceCallingLocsList.push_back(AVE.LocationName);
15930  }
15931  else if(AVE.FormatType == TimeTimeLoc)
15932  {
15933  ServiceCallingLocsList.push_back(AVE.LocationName);
15934  }
15935  else if(AVE.Command == "cdt") //list if not next to start or finish
15936  {
15937  if(ActionVector.at(z-1).SequenceType == Start)
15938  {
15939  continue;
15940  }
15941  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
15942  {
15943  continue;
15944  }
15945  else
15946  {
15947  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
15948  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
15949  }
15950  }
15951  else if(AVE.FormatType == ExitRailway) //Fer
15952  {
15953  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
15954  AnsiString LName = TE.ActiveTrackElementName;
15955  if(LName != "")
15956  {
15957  ServiceCallingLocsList.push_back(LName);
15958  }
15959  else
15960  {
15961  int HLoc = TE.HLoc;
15962  int VLoc = TE.VLoc;
15963  AnsiString HString;
15964  AnsiString VString;
15965  if(HLoc < 0)
15966  {
15967  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15968  }
15969  else
15970  {
15971  HString = AnsiString(HLoc);
15972  }
15973  if(VLoc < 0)
15974  {
15975  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15976  }
15977  else
15978  {
15979  VString = AnsiString(VLoc);
15980  }
15981  ServiceCallingLocsList.push_back(HString + '-' + VString);
15982  }
15983  }
15984  }
15985  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
15986  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
15987  }
15988  //AllServiceCallingLocsMap built
15989 
15990  //test validity of AllServiceCallingLocsMap
15991 /*
15992  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
15993  std::ofstream Test(TestFile.c_str());
15994 
15995  if(TestFile == 0)
15996  {
15997  ShowMessage("TestFile failed to open - can't be created");
15998  Utilities->CallLogPop();
15999  return false;
16000  }
16001 
16002  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
16003  {
16004  Test << ASCLIt->first << '\n'; //service ref
16005  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
16006  {
16007  Test << *SCLIt << '\n';
16008  }
16009  Test << "\n\n";
16010  }
16011  Test.close();
16012  Utilities->CallLogPop();
16013  return true;
16014 */
16015 
16016  //initialise variables before calc LastTTTime & build LocServiceTimesVector
16017  if(TrainDataVector.empty())
16018  {
16019  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
16020  Utilities->CallLogPop(2209);
16021  return(false);
16022  }
16023  TLocServiceTimes TLSTEntry;
16024  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
16025  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
16026  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
16027  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
16028  LastTTTime = "";
16029 
16030  //calculate LastTTTime
16031  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16032  {
16033  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16034  TActionVector &ActionVector = TrainDataEntry.ActionVector;
16035  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
16036  TDateTime LastTDTime;
16037  int IncMinutes = 0;
16038  NumTrains = TrainDataEntry.NumberOfTrains;
16039  if(ActionVector.empty())
16040  {
16041  continue;
16042  }
16043  if(ActionVector.at(0).SignallerControl)
16044  {
16045  continue;
16046  }
16047  if(AVLast->FormatType == Repeat)
16048  {
16049  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16050  AVLast--; //now points to the command before the repeat
16051  }
16052  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
16053  {
16054  AVLast--; //points to last timed entry
16055  }
16056  //here AVLast points to last entry with a time
16057  if(AVLast->ArrivalTime != TDateTime(-1))
16058  {
16059  LastTDTime = AVLast->ArrivalTime;
16060  }
16061  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
16062  {
16063  LastTDTime = AVLast->EventTime;
16064  }
16065  else
16066  {
16067  continue; //shouldn't ever reach here but if do then skip this service
16068  }
16069  if(NumTrains == 1)
16070  {
16071  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
16072  }
16073  else
16074  {
16075  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
16076  }
16077  if(LastAnsiTime > LastTTTime)
16078  {
16079  LastTTTime = LastAnsiTime;
16080  }
16081  }
16082 
16083 //build LocServiceTimesVector
16084 
16085 /* struct TLocServiceTimes
16086  {
16087  AnsiString Location;
16088  AnsiString ServiceAndRepeatNum;
16089  AnsiString AtLocTime;
16090  AnsiString ArrTime;
16091  AnsiString DepTime;
16092  };
16093  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16094 
16095 This works as follows:
16096 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
16097 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
16098 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
16099 
16100 Every action for every train is examined and times entered as follows:-
16101 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
16102 b) an unlocated Snt: entry time becomes DepTime
16103 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
16104 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
16105 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
16106 f) TimeTimeLoc: Arrival time entered as ErrTime, a check if Arr & Dep same and if s go in as one entry, else all minutes between entered as AtLocs then DepTime
16107 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
16108 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
16109 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
16110 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
16111 */
16112  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16113  {
16114  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16115  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16116  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
16117  int IncMinutes = 0;
16118  NumTrains = TrainDataEntry.NumberOfTrains;
16119  if(ActionVector.empty())
16120  {
16121  continue;
16122  }
16123  if(ActionVector.at(0).SignallerControl)
16124  {
16125  continue;
16126  }
16127  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16128  {
16129  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16130  }
16131  for(int y = 0; y < NumTrains; y++)
16132  {
16133  if(NumTrains == 1)
16134  {
16135  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
16136  }
16137  else if(y == 0)
16138  {
16139  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
16140  }
16141  else
16142  {
16143  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
16144  }
16145  for(unsigned int z = 0; z < ActionVector.size(); z++)
16146  {
16147  TActionVectorEntry AVE = ActionVector.at(z);
16148  TLSTEntry.AtLocTime = "";
16149  TLSTEntry.ArrTime = "";
16150  TLSTEntry.DepTime = "";
16151  TLSTEntry.Location = "";
16152  TLSTEntry.FrhMarker = "";
16153 
16154  if(AVE.FormatType == StartNew) //Snt only
16155  {
16156  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
16157  {
16158  TLSTEntry.Location = AVE.LocationName;
16159  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
16160  LocServiceTimesVector.push_back(TLSTEntry);
16161 
16162  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16163  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
16164  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16165  {
16166  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16167  {
16168  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
16169  break;
16170  }
16171  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16172  {
16173  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
16174  break;
16175  }
16176  }
16177  if(FoundStopTime == "")
16178  {
16179  throw Exception("Failure to determine FoundStopTime for located Snt");
16180  }
16181  int WhileCount = 0;
16182  while(true)
16183  {
16184  //add minutes until reach FoundStopTime but don't add that time
16185  WhileCount++;
16186  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16187  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16188  TLSTEntry.DepTime = "";
16189  TLSTEntry.ArrTime = "";
16190  if(IncTime >= FoundStopTime) //don't add that time
16191  {
16192  break;
16193  }
16194  LocServiceTimesVector.push_back(TLSTEntry);
16195  if(WhileCount > 2000)
16196  {
16197  throw Exception("While loop failed to break in 2000 loops for located Snt");
16198  }
16199  }
16200  }
16201  else //unlocated Snt, use the EventTime as DepTime for this vector
16202  {
16204  if(TE.ActiveTrackElementName != "")
16205  {
16206  TLSTEntry.Location = TE.ActiveTrackElementName;
16207  }
16208  else
16209  {
16210  int HLoc = TE.HLoc;
16211  int VLoc = TE.VLoc;
16212  AnsiString HString;
16213  AnsiString VString;
16214  if(HLoc < 0)
16215  {
16216  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16217  }
16218  else
16219  {
16220  HString = AnsiString(HLoc);
16221  }
16222  if(VLoc < 0)
16223  {
16224  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16225  }
16226  else
16227  {
16228  VString = AnsiString(VLoc);
16229  }
16230  TLSTEntry.Location = HString + '-' + VString;
16231  }
16232  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
16233  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16234  LocServiceTimesVector.push_back(TLSTEntry);
16235  }
16236  }
16237 
16238  else if(AVE.SequenceType == Start) //other start entries, all located
16239  {
16240  TLSTEntry.Location = AVE.LocationName;
16241  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
16242  LocServiceTimesVector.push_back(TLSTEntry);
16243  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16244  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16245  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16246  {
16247  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16248  {
16249  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
16250  break;
16251  }
16252  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16253  {
16254  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
16255  break;
16256  }
16257  }
16258  if(FoundStopTime == "")
16259  {
16260  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16261  }
16262  int WhileCount = 0;
16263  while(true)
16264  {
16265  //add minutes until reach FoundStopTime but don't add that time
16266  WhileCount++;
16267  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16268  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16269  TLSTEntry.DepTime = "";
16270  TLSTEntry.ArrTime = "";
16271  if(IncTime >= FoundStopTime) //don't add that time
16272  {
16273  break;
16274  }
16275  LocServiceTimesVector.push_back(TLSTEntry);
16276  if(WhileCount > 2000)
16277  {
16278  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16279  }
16280  }
16281  }
16282 
16283  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if ar5rival add in all mins to the departure or finish
16284  {
16285  TLSTEntry.Location = AVE.LocationName;
16286  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
16287  {
16288  bool SkipAddingMinutes = false;
16289  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
16290  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16291  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
16292  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16293  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16294  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16295  {
16296  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16297  {
16298  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
16299  break;
16300  }
16301  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16302  {
16303  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
16304  if((a == z + 1) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
16305  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
16306  {
16307  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
16308  SkipAddingMinutes = true;
16309  }
16310  break;
16311  }
16312  }
16313  if(FoundStopTime == "")
16314  {
16315  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16316  }
16317  if(!SkipAddingMinutes)
16318  {
16319  int WhileCount = 0;
16320  while(true)
16321  {
16322  //add minutes until reach FoundStopTime but don't add that time
16323  WhileCount++;
16324  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16325  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16326  TLSTEntry.DepTime = "";
16327  TLSTEntry.ArrTime = "";
16328  if(IncTime >= FoundStopTime) //don't add that time
16329  {
16330  break;
16331  }
16332  LocServiceTimesVector.push_back(TLSTEntry);
16333  if(WhileCount > 2000)
16334  {
16335  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16336  }
16337  }
16338  }
16339  }
16340  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
16341  {
16342  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
16343  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16344  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
16345  {
16346  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
16347  {
16348  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
16349  LocServiceTimesVector.pop_back();
16350  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
16351  }
16352  else //just add the dep & atloc times
16353  {
16354  TLSTEntry.ArrTime = "";
16355  LocServiceTimesVector.push_back(TLSTEntry);
16356  }
16357  }
16358  else //just add the dep & atloc times
16359  {
16360  TLSTEntry.ArrTime = "";
16361  LocServiceTimesVector.push_back(TLSTEntry);
16362  }
16363  }
16364  }
16365 
16366  else if(AVE.FormatType == TimeTimeLoc)
16367  {
16368  TLSTEntry.Location = AVE.LocationName;
16369  if(AVE.ArrivalTime > TDateTime(-1)) //should be
16370  {
16371  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
16372  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16373  }
16374  if(AVE.DepartureTime > TDateTime(-1)) //should be
16375  {
16376  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
16377  }
16378  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
16379  {
16380  LocServiceTimesVector.push_back(TLSTEntry);
16381  }
16382  else
16383  {
16384  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
16385  TLSTEntry.DepTime = "";
16386  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
16387  TLSTEntry.ArrTime = ""; //done with this now
16388  while(TLSTEntry.AtLocTime < TempDepTime)
16389  {
16390  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16391  if(TLSTEntry.AtLocTime == TempDepTime)
16392  {
16393  TLSTEntry.DepTime = TempDepTime; //restore value
16394  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
16395  }
16396  else
16397  {
16398  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
16399  }
16400  }
16401  }
16402  }
16403 
16404  else if(AVE.FormatType == ExitRailway) //Fer
16405  {
16406  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
16407  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
16408  if(LName != "")
16409  {
16410  TLSTEntry.Location = LName;
16411  }
16412  else
16413  {
16414  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
16415  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
16416  AnsiString HString;
16417  AnsiString VString;
16418  if(HLoc < 0)
16419  {
16420  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16421  }
16422  else
16423  {
16424  HString = AnsiString(HLoc);
16425  }
16426  if(VLoc < 0)
16427  {
16428  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16429  }
16430  else
16431  {
16432  VString = AnsiString(VLoc);
16433  }
16434  TLSTEntry.Location = HString + '-' + VString;
16435  }
16436  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
16437  }
16438 
16439  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
16440  {
16441  AnsiString FrhTime;
16442  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16443  {
16444  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16445  }
16446  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16447  {
16448  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16449  }
16450  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
16451  TLSTEntry.Location = AVE.LocationName;
16452  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16453  TLSTEntry.FrhMarker = "Frh";
16454  LocServiceTimesVector.push_back(TLSTEntry);
16455  TLSTEntry.FrhMarker = "";
16456  //add all times from next minute to end of timetable
16457  while(IncTime <= LastTTTime)
16458  {
16459  TLSTEntry.AtLocTime = IncTime;
16460  LocServiceTimesVector.push_back(TLSTEntry);
16461  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16462  }
16463  }
16464 
16465  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
16466  {
16467  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
16468  {
16469  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
16470  TLSTEntry.Location = AVE.LocationName;
16471  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16472  TLSTEntry.FrhMarker = "Frh";
16473  LocServiceTimesVector.push_back(TLSTEntry);
16474  TLSTEntry.FrhMarker = "";
16475  //add all times from next minute to end of timetable
16476  while(IncTime <= LastTTTime)
16477  {
16478  TLSTEntry.AtLocTime = IncTime;
16479  LocServiceTimesVector.push_back(TLSTEntry);
16480  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16481  }
16482  }
16483  }
16484 
16485  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
16486  {
16487  //nothing is done here as the entry will be listed at this time under the new service reference
16488  }
16489  }
16490  }
16491  }
16492 
16493  //now sort in location order
16494  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
16495  //LocServiceTimesVector now complete & sorted in location order
16496 
16497  //declare pointers for use in printouts
16498  TLocServiceTimesVector::iterator Ptr1, Ptr2;
16499 
16500  //set up the output file
16501  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16502  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
16503 
16504  std::ofstream TTFile3(TTFileName3.c_str());
16505 
16506  if(TTFile3 == 0)
16507  {
16508  ShowMessage("Conflict Analysis file failed to open - can't be created");
16509  Utilities->CallLogPop(2210);
16510  return(false);
16511  }
16512  if(LocServiceTimesVector.empty())
16513  {
16514  ShowMessage("No timetabled services found");
16515  TTFile3.close();
16516  DeleteFile(TTFileName3);
16517  Utilities->CallLogPop(2211);
16518  return(false);
16519  }
16520  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n\n\n";
16521 
16522 
16523  //arrivals
16524  if(ArrChecked)
16525  {
16526  //sort in ArrTime order for each location
16527  Ptr1 = LocServiceTimesVector.begin();
16528  Ptr2 = Ptr1 + 1;
16529  while(Ptr2 != LocServiceTimesVector.end())
16530  {
16531  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16532  {
16533  Ptr2++;
16534  if(Ptr2 == LocServiceTimesVector.end())
16535  {
16536  break;
16537  }
16538  }
16539  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
16540  Ptr1 = Ptr2; //first entry with next name
16541  if(Ptr2 != LocServiceTimesVector.end())
16542  {
16543  Ptr2++;
16544  }
16545  }
16546 
16547  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
16548 
16549  TTFile3 << "Arrival analysis: an asterisk means that the number of same approach code arrivals is equal to or greater than the number of platforms.\n";
16550  TTFile3 << "If the total number of arrivals exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16551  MinuteString = " minutes";
16552  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16553  if(ArrRange == 1)
16554  {
16555  MinuteString = " minute";
16556  }
16557  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
16558  TTFile3 << ",Platforms,Trains\n\n";
16559 
16560  Ptr1 = LocServiceTimesVector.begin();
16561  Ptr2 = Ptr1 + 1;
16562  while(Ptr2 != LocServiceTimesVector.end())
16563  {
16564  PreviousService = "";
16565  NumTrainsAtLoc = 0;
16566  ServiceAndRepeatNumTotal = "";
16567  NumPlats = 0;
16568  NumPlatsAtThisLocCalculated = false;
16569  BasicTime = "";
16570  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16571  {
16572  PreviousService = "";
16573  NumTrainsAtLoc = 0;
16574  ServiceAndRepeatNumTotal = "";
16575  NumPlats = 0;
16576  NumPlatsAtThisLocCalculated = false;
16577  BasicTime = "";
16578  Ptr1++;
16579  Ptr2++;
16580  if(Ptr2 == LocServiceTimesVector.end())
16581  {
16582  break;
16583  }
16584  }
16585  if(Ptr2 == LocServiceTimesVector.end())
16586  {
16587  break;
16588  }
16589  while(Ptr2->Location == Ptr1->Location)
16590  {
16591  PreviousService = "";
16592  NumTrainsAtLoc = 0;
16593  ServiceAndRepeatNumTotal = "";
16594  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
16595  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16596  {
16597  break;
16598  }
16599  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
16600  {
16601  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
16602  Ptr1++;
16603  Ptr2++;
16604  if(Ptr2 == LocServiceTimesVector.end())
16605  {
16606  break;
16607  }
16608  if(Ptr2->Location != Ptr1->Location)
16609  {
16610  break;
16611  }
16612  }
16613  if(Ptr2 == LocServiceTimesVector.end())
16614  {
16615  break;
16616  }
16617  if(Ptr2->Location != Ptr1->Location)
16618  {
16619  break;
16620  }
16621  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
16622  {
16623  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
16624  {
16625  break;
16626  }
16627  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16628  {
16629  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
16630  NumPlatsAtThisLocCalculated = true;
16631  }
16632  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16633  {
16634  if(ServiceAndRepeatNumTotal == "")
16635  {
16636  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
16637  NumTrainsAtLoc = 1;
16638  }
16639  else
16640  {
16641  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
16642  }
16643  }
16644  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16645  if(ServiceAndRepeatNumTotal == "")
16646  {
16647  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
16648  NumTrainsAtLoc = 1;
16649  }
16650  else
16651  {
16652  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
16653  }
16654  Ptr1 = Ptr2;
16655  Ptr2++;
16656  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
16657  {
16658  int MaxNumberOfSameDirections = 0;
16659  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16660  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16661  {
16662 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16663  TTFile3.close();
16664  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16665 // Utilities->CallLogPop(2224);
16666 // return false;
16667  }
16668  AnsiString Asterisk = "";
16669  if(MaxNumberOfSameDirections >= NumPlats)
16670  {
16671  Asterisk = "* ";
16672  }
16673  //print out a single line for number of trains at loc with all service refs
16674  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16675  ArrivalsPrinted = true;
16676  ServiceAndRepeatNumTotal = "";
16677  }
16678  if(Ptr2 == LocServiceTimesVector.end())
16679  {
16680  break;
16681  }
16682  if(Ptr2->Location != Ptr1->Location)
16683  {
16684  break;
16685  }
16686  }
16687  if(Ptr2 == LocServiceTimesVector.end())
16688  {
16689  break;
16690  }
16691  }
16692  }
16693  if(!ArrivalsPrinted)
16694  {
16695  TTFile3 << "Nothing to report for arrivals";
16696  }
16697  TTFile3 << "\n\n\n";
16698  }
16699  //end of routine for arrivals
16700 
16701  //departures
16702  if(DepChecked)
16703  {
16704  //sort in DepTime order for each location
16705  Ptr1 = LocServiceTimesVector.begin();
16706  Ptr2 = Ptr1 + 1;
16707  while(Ptr2 != LocServiceTimesVector.end())
16708  {
16709  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16710  {
16711  Ptr2++;
16712  if(Ptr2 == LocServiceTimesVector.end())
16713  {
16714  break;
16715  }
16716  }
16717  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
16718  Ptr1 = Ptr2; //first entry with next name
16719  if(Ptr2 != LocServiceTimesVector.end())
16720  {
16721  Ptr2++;
16722  }
16723  }
16724 
16725  //routine for departures - number of trains departing within the specified range with services listed at the end
16726  TTFile3 << "Departure analysis: an asterisk means that the number of same exit code departures is equal to or greater than the number of platforms.\n";
16727  TTFile3 << "If the total number of departures exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16728  MinuteString = " minutes";
16729  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16730  if(DepRange == 1)
16731  {
16732  MinuteString = " minute";
16733  }
16734  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
16735  TTFile3 << ",Platforms,Trains\n\n";
16736 
16737  Ptr1 = LocServiceTimesVector.begin();
16738  Ptr2 = Ptr1 + 1;
16739  while(Ptr2 != LocServiceTimesVector.end())
16740  {
16741  PreviousService = "";
16742  NumTrainsAtLoc = 0;
16743  ServiceAndRepeatNumTotal = "";
16744  NumPlats = 0;
16745  NumPlatsAtThisLocCalculated = false;
16746  BasicTime = "";
16747  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16748  {
16749  PreviousService = "";
16750  NumTrainsAtLoc = 0;
16751  ServiceAndRepeatNumTotal = "";
16752  NumPlats = 0;
16753  NumPlatsAtThisLocCalculated = false;
16754  BasicTime = "";
16755  Ptr1++;
16756  Ptr2++;
16757  if(Ptr2 == LocServiceTimesVector.end())
16758  {
16759  break;
16760  }
16761  }
16762  if(Ptr2 == LocServiceTimesVector.end())
16763  {
16764  break;
16765  }
16766  while(Ptr2->Location == Ptr1->Location)
16767  {
16768  PreviousService = "";
16769  NumTrainsAtLoc = 0;
16770  ServiceAndRepeatNumTotal = "";
16771  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
16772  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16773  {
16774  break;
16775  }
16776  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
16777  {
16778  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
16779  Ptr1++;
16780  Ptr2++;
16781  if(Ptr2 == LocServiceTimesVector.end())
16782  {
16783  break;
16784  }
16785  if(Ptr2->Location != Ptr1->Location)
16786  {
16787  break;
16788  }
16789  }
16790  if(Ptr2 == LocServiceTimesVector.end())
16791  {
16792  break;
16793  }
16794  if(Ptr2->Location != Ptr1->Location)
16795  {
16796  break;
16797  }
16798  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
16799  {
16800  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
16801  {
16802  break;
16803  }
16804  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16805  {
16806  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
16807  NumPlatsAtThisLocCalculated = true;
16808  }
16809  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16810  {
16811  if(ServiceAndRepeatNumTotal == "")
16812  {
16813  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16814  NumTrainsAtLoc = 1;
16815  }
16816  else
16817  {
16818  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16819  }
16820  }
16821  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16822  if(ServiceAndRepeatNumTotal == "")
16823  {
16824  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16825  NumTrainsAtLoc = 1;
16826  }
16827  else
16828  {
16829  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16830  }
16831  Ptr1 = Ptr2;
16832  Ptr2++;
16833  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
16834  {
16835  int MaxNumberOfSameDirections = 0;
16836  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16837  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16838  {
16839 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16840  TTFile3.close();
16841  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16842 // Utilities->CallLogPop(2225);
16843 // return false;
16844  }
16845  AnsiString Asterisk = "";
16846  if(MaxNumberOfSameDirections >= NumPlats)
16847  {
16848  Asterisk = "* ";
16849  }
16850  //print out a single line for number of trains at loc with all service refs
16851  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16852  DeparturesPrinted = true;
16853  ServiceAndRepeatNumTotal = "";
16854  }
16855  if(Ptr2 == LocServiceTimesVector.end())
16856  {
16857  break;
16858  }
16859  if(Ptr2->Location != Ptr1->Location)
16860  {
16861  break;
16862  }
16863  }
16864  if(Ptr2 == LocServiceTimesVector.end())
16865  {
16866  break;
16867  }
16868  }
16869  }
16870  if(!DeparturesPrinted)
16871  {
16872  TTFile3 << "Nothing to report for departures";
16873  }
16874  TTFile3 << "\n\n\n";
16875  }
16876  //end of routine for departures
16877 
16878 
16879  //list trains at locations at same time
16880 
16881  if(AtLocChecked)
16882  {
16883  //sort in AtLocTime order for each location
16884  Ptr1 = LocServiceTimesVector.begin();
16885  Ptr2 = Ptr1 + 1;
16886  while(Ptr2 != LocServiceTimesVector.end())
16887  {
16888  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16889  {
16890  Ptr2++;
16891  if(Ptr2 == LocServiceTimesVector.end())
16892  {
16893  break;
16894  }
16895  }
16896  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
16897  Ptr1 = Ptr2; //first entry with next name
16898  if(Ptr2 != LocServiceTimesVector.end())
16899  {
16900  Ptr2++;
16901  }
16902  }
16903 
16904  //print out simultaneous AtLocs (don't need range of times for AtLocs)
16905  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
16906  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
16907  TTFile3 << ",Platforms,Trains,\n\n";
16908  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16909  Ptr1 = LocServiceTimesVector.begin();
16910  Ptr2 = Ptr1 + 1;
16911  while(Ptr2 != LocServiceTimesVector.end())
16912  {
16913  PreviousService = "";
16914  ServiceAndRepeatNumTotal = "";
16915  NumTrainsAtLoc = 0;
16916  NumPlats = 0;
16917  NumPlatsAtThisLocCalculated = false;
16918  FrhCount = 0;
16919  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16920  {
16921  PreviousService = "";
16922  ServiceAndRepeatNumTotal = "";
16923  NumTrainsAtLoc = 0;
16924  NumPlats = 0;
16925  NumPlatsAtThisLocCalculated = false;
16926  FrhCount = 0;
16927  Ptr1++;
16928  Ptr2++;
16929  if(Ptr2 == LocServiceTimesVector.end())
16930  {
16931  break;
16932  }
16933  }
16934  if(Ptr2 == LocServiceTimesVector.end())
16935  {
16936  break;
16937  }
16938  while(Ptr2->Location == Ptr1->Location)
16939  {
16940  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
16941  {
16942  FrhCount++;
16943  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16944  }
16945  PreviousService = "";
16946  NumTrainsAtLoc = 0;
16947  ServiceAndRepeatNumTotal = "";
16948  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16949  {
16950  break;
16951  }
16952  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
16953  {
16954  Ptr1++;
16955  if(Ptr1->FrhMarker == "Frh")
16956  {
16957  FrhCount++;
16958  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16959  }
16960  Ptr2++;
16961  if(Ptr2 == LocServiceTimesVector.end())
16962  {
16963  break;
16964  }
16965  if(Ptr2->Location != Ptr1->Location)
16966  {
16967  break;
16968  }
16969  }
16970  if(Ptr2 == LocServiceTimesVector.end())
16971  {
16972  break;
16973  }
16974  if(Ptr2->Location != Ptr1->Location)
16975  {
16976  break;
16977  }
16978  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
16979  {
16980  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
16981  {
16982  break;
16983  }
16984  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16985  {
16986  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
16987  NumPlatsAtThisLocCalculated = true;
16988  }
16989  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16990  {
16991  if(ServiceAndRepeatNumTotal == "")
16992  {
16993  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
16994  NumTrainsAtLoc = 1;
16995  }
16996  else
16997  {
16998  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
16999  }
17000  }
17001  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
17002  if(ServiceAndRepeatNumTotal == "")
17003  {
17004  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
17005  NumTrainsAtLoc = 1;
17006  }
17007  else
17008  {
17009  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
17010  }
17011  Ptr1 = Ptr2;
17012  if(Ptr1->FrhMarker == "Frh")
17013  {
17014  FrhCount++;
17015  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17016  }
17017  Ptr2++;
17018  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
17019  {
17020 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
17021 //new text //don't print out if all remainers or if only 1 train at loc
17022  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
17023 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
17024 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
17025  {
17026  AnsiString Asterisk = "";
17027  if(NumTrainsAtLoc > NumPlats)
17028  {
17029  Asterisk = "* ";
17030  }
17031  //print out a single line for number of trains at loc with all service refs
17032  if(FrhCount == 0)
17033  {
17034  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
17035  }
17036  else if(FrhCount == 1)
17037  {
17038  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
17039  }
17040  else
17041  {
17042  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
17043  }
17044  LastFrhCount = FrhCount;
17045  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
17046  AtLocsPrinted = true;
17047  ServiceAndRepeatNumTotal = "";
17048  }
17049  }
17050  if(Ptr2 == LocServiceTimesVector.end())
17051  {
17052  break;
17053  }
17054  if(Ptr2->Location != Ptr1->Location)
17055  {
17056  break;
17057  }
17058  }
17059  if(Ptr2 == LocServiceTimesVector.end())
17060  {
17061  break;
17062  }
17063  }
17064  }
17065  if(!AtLocsPrinted)
17066  {
17067  TTFile3 << "Nothing to report for trains at locations";
17068  }
17069  TTFile3 << "\n\n\n";
17070  //end of simultaneous AtLocs
17071 
17072 /*
17073  //print out the full vector here for testing purposes
17074  TTFile3 << "Full LocServiceTimesVector\n\n";
17075  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
17076 
17077  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
17078  {
17079  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
17080  }
17081 
17082  TTFile3 << "\n\n\n";
17083 */
17084  }
17085  TTFile3.close();
17086  Utilities->CallLogPop(2212);
17087  return(true);
17088  }
17089 
17090  catch(const Exception &e)
17091  {
17092  AnsiString TTErrorFileName = "Analysis Error.txt";
17093  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
17094  std::ofstream TTError(TTErrorFileName.c_str());
17095  if(TTError == 0)
17096  {
17097  ShowMessage("Analysis error file failed to open - can't be created");
17098  Utilities->CallLogPop(2233);
17099  return(false);
17100  }
17101  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17102  TTError << TimeNow.c_str() << "\n" << ArrRange << "\n" << ArrChecked << "\n" << DepRange << "\n" <<
17103  DepChecked << "\n" << AtLocChecked << "\n" << AnsiString(e.Message);
17104  TTError.close();
17105  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
17106  Utilities->CallLogPop(2226);
17107  return(false);
17108  }
17109 }
17110 
17111 // ---------------------------------------------------------------------------
17112 
17113 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
17114 {
17115 //convert times to integer minutes
17116  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
17117  if((Time1 == "") || (Time2 == ""))
17118  {
17119  Utilities->CallLogPop(2213);
17120  return(false);
17121  }
17122  int Mins = Time1.SubString(4,2).ToInt();
17123  int Hours = Time1.SubString(1,2).ToInt();
17124  int Time1Mins = (Hours * 60) + Mins;
17125  Mins = Time2.SubString(4,2).ToInt();
17126  Hours = Time2.SubString(1,2).ToInt();
17127  int Time2Mins = (Hours * 60) + Mins;
17128  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
17129  {
17130  Utilities->CallLogPop(2214);
17131  return(true);
17132  }
17133  Utilities->CallLogPop(2215);
17134  return(false);
17135 }
17136 
17137 // ---------------------------------------------------------------------------
17138 
17139 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
17140  bool &AnalysisError, int &MaxNumberOfSameDirections)
17141 {
17142  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
17143 
17144  try
17145  {
17146  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
17147  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
17148  int SCPos = 0;
17149  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
17150  //first change every second comma in Input to a semicolon so can separate services but keep times with services
17151  bool EvenComma = false;
17152  for(int x = 1; x <= Input.Length(); x++)
17153  {
17154  TempStr1 = Input[x];
17155  if(TempStr1 == AnsiString(',') && EvenComma)
17156  {
17157  TempStr2 += ';';
17158  }
17159  else
17160  {
17161  TempStr2 += Input[x];
17162  }
17163  if(TempStr1 == AnsiString(','))
17164  {
17165  EvenComma = !EvenComma;
17166  }
17167  }
17168  //load up the list of services with associated times
17169  while(TempStr2.Length() > 0)
17170  {
17171  SCPos = TempStr2.Pos(';');
17172  if(SCPos > 0) //0 if not found, as won't be when only one service left
17173  {
17174  OneService = TempStr2.SubString(1, SCPos - 1);
17175  ServiceList.push_back(OneService);
17176  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
17177  }
17178  else //no semicolon so looking at last (or only) element
17179  {
17180  ServiceList.push_back(TempStr2);
17181  TempStr2 = "";
17182  }
17183  }
17184  ServiceList.sort(); // alphabetical order
17185  ServiceList.unique(); //remove duplicates
17186  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
17187 
17188  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
17189  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
17190  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
17191  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
17192 
17193  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17194  {
17195  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
17196  }
17197  SLIt3 = ServiceList.end();
17198  SLIt3--; //so points to last element
17199  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
17200  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
17201  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
17202  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
17203  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
17204  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
17205 
17206  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
17207  {
17208  SLIt = SLIt1;
17209  SLIt++; //so points to one after SLIt1
17210  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
17211  {
17212  continue; //already allocated so skip to the next
17213  }
17214  else
17215  {
17216  CommaPos1 = SLIt1->Pos(','); //can't be 0
17217  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
17218  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
17219  SpacePos = ServiceRef1.Pos(' ');
17220  RepeatNum1 = 0;
17221  if(SpacePos > 0) //otherwise it's already correct
17222  {
17223  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
17224  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
17225  if(RepeatInfo1[1] == 'F')
17226  {
17227  RepeatNum1 = 0;
17228  }
17229  else
17230  {
17231  SpacePos = RepeatInfo1.Pos(' ');
17232  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
17233  }
17234  }
17235  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
17236  //but this includes the "&0" etc so need to strip these
17237  AmpersandPos = AnsiTime1.Pos('&');
17238  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
17239 
17240  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
17241  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
17242  {
17243  throw Exception("ASCLIt1 Error in " + Input);
17244  }
17245  ServiceCallingLocsList1 = ASCLIt1->second;
17246  AmpersandPos = SLIt1->Pos('&');
17247  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17248  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
17249 
17250  SameDirectionCount = 1;
17251  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
17252  {
17253  CommaPos2 = SLIt2->Pos(','); //can't be 0
17254  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
17255  //but this contains "(First service..." etc so need to strip these
17256  SpacePos = ServiceRef2.Pos(' ');
17257  RepeatNum2 = 0;
17258  if(SpacePos > 0) //otherwise it's already correct
17259  {
17260  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
17261  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
17262  if(RepeatInfo2[1] == 'F')
17263  {
17264  RepeatNum2 = 0;
17265  }
17266  else
17267  {
17268  SpacePos = RepeatInfo2.Pos(' ');
17269  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
17270  }
17271  }
17272  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
17273  //but this includes the "&0" etc so need to strip these
17274  AmpersandPos = AnsiTime2.Pos('&');
17275  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
17276 
17277  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
17278  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
17279  {
17280  throw Exception("ASCLIt2 Error in " + Input);
17281  }
17282  ServiceCallingLocsList2 = ASCLIt2->second;
17283  //now compare the two
17284  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
17285  {
17286  int AmpersandPos = SLIt2->Pos('&');
17287  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17288  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
17289  SameDirectionCount++;
17290  }
17291  }
17292  if(SameDirectionCount > MaxNumberOfSameDirections)
17293  {
17294  MaxNumberOfSameDirections = SameDirectionCount;
17295  }
17296  }
17297  }
17298 
17299  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
17300  {
17301  //any existing direction so allocate it now
17302  AmpersandPos = SLIt3->Pos('&');
17303  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17304  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
17305  }
17306  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
17307  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17308  {
17309  //extract the DirectionMarker as an integer
17310  AmpersandPos = SLIt->Pos('&');
17311  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
17312  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
17313  DirectionMarker = DirectionMarkerString.ToInt();
17314  AnsiString DirectionSuffix = "";
17315  char c;
17316  if(DirectionMarker < 27)
17317  {
17318  c = 64 + DirectionMarker; //so 1 -> 'A'
17319  DirectionSuffix = "," + AnsiString(c);
17320  }
17321  else if(DirectionMarker < 53)
17322  {
17323  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
17324  DirectionSuffix = ",A" + AnsiString(c);
17325  }
17326  else
17327  {
17328  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
17329  }
17330  *SLIt = ServiceWithoutMarker + DirectionSuffix;
17331  }
17332  //now prepare the final consolidated output
17333  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17334  {
17335  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
17336  }
17337  if(Output.Length() > 0)
17338  {
17339  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
17340  }
17341  Utilities->CallLogPop(2216);
17342  return(Output);
17343  }
17344 
17345  catch(const Exception &e)
17346  {
17347  AnalysisError = true;
17348  Utilities->CallLogPop(2227);
17349  return(e.Message);
17350  }
17351 }
17352 
17353 // ---------------------------------------------------------------------------
17354 
17355 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
17356 {
17357  //similar to above but doesn't include times in the input
17358  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
17359  AnsiString InternalInput = Input, Output = "", OneService = "";
17360  int CommaPos = 0;
17361  std::list<AnsiString> ServiceList;
17362  //load up the list
17363  while(InternalInput.Length() > 0)
17364  {
17365  CommaPos = InternalInput.Pos(',');
17366  if(CommaPos > 0) //0 if not found, as won't be when only one service left
17367  {
17368  OneService = InternalInput.SubString(1, CommaPos - 1);
17369  ServiceList.push_back(OneService);
17370  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
17371  }
17372  else //no comma so looking at last (or only) element
17373  {
17374  ServiceList.push_back(InternalInput);
17375  InternalInput = "";
17376  }
17377  }
17378 
17379  ServiceList.sort(); // alphabetical order
17380  ServiceList.unique(); //remove duplicates
17381  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
17382  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17383  {
17384  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
17385  }
17386  if(Output.Length() > 0)
17387  {
17388  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
17389  }
17390  Utilities->CallLogPop(2217);
17391  return(Output);
17392 }
17393 
17394 // ---------------------------------------------------------------------------
17395 
17396 
17397 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
17398  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
17399 {
17400  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
17401  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
17402 
17403  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
17404  //general list pointers, LocPtrs point to Location in the two lists
17405 
17406  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
17407  //for List1
17408  bool LocFound = false;
17409  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
17410  int IncMinutes;
17411  TDateTime FirstServiceTime;
17412 
17413  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
17414  int Ref1Target = 0, Ref1Count = 0;
17415  int SlashPos = Ref1.Pos('/');
17416  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
17417  {
17418  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
17419  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
17420  }
17421  int Ref2Target = 0, Ref2Count = 0;
17422  SlashPos = Ref2.Pos('/');
17423  if(SlashPos > 0) //if 0 leave as is
17424  {
17425  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
17426  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
17427  }
17428  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
17429  {
17430  //even if others have same names. But if there are cdt's then need to refind the correct service
17431  if((*ListPtr1) == Location) //
17432  {
17433  LocPtr1 = ListPtr1; //may be modified later
17434  LocFound = true;
17435  }
17436  if(ListPtr1->SubString(1, 3) == "%%%")
17437  {
17438  AnsiString CDTTime = ListPtr1->SubString(4, 5);
17439  //now adjust the time to correspond to the repeat if there is one
17440  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
17441  {
17442  IncMinutes = -1;
17443  FirstServiceTime = TDateTime(-1);
17444  bool BreakFlag = false;
17445  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
17446  {
17447  if(TDVIt->ServiceReference == Ref1)
17448  {
17449  if(Ref1Target > Ref1Count)
17450  {
17451  Ref1Count++;
17452  continue;
17453  }
17454  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
17455  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
17456  {
17457  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
17458  {
17459  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
17460  BreakFlag = true;
17461  break;
17462  }
17463  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
17464  {
17465  FirstServiceTime = AVIt->ArrivalTime;
17466  BreakFlag = true;
17467  break;
17468  }
17469  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
17470  {
17471  FirstServiceTime = AVIt->DepartureTime;
17472  BreakFlag = true;
17473  break;
17474  }
17475  }
17476  if(BreakFlag)
17477  {
17478  break;
17479  }
17480  }
17481  }
17482  if(IncMinutes == -1)
17483  {
17484  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17485  }
17486  if(FirstServiceTime == TDateTime(-1))
17487  {
17488  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17489  }
17490  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
17491  }
17492  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
17493  {
17494  LocFound = false;
17495  continue;
17496  }
17497  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
17498  {
17499  break;
17500  }
17501  if(Time1 > CDTTime) //not there yet so go on
17502  {
17503  LocFound = false;
17504  continue;
17505  }
17506  if(Time1 < CDTTime) //gone too far so can stop now
17507  {
17508  break;
17509  }
17510  }
17511  }
17512  if(!LocFound) //have to find it in both lists
17513  {
17514  Utilities->CallLogPop(2228);
17515  return( false);
17516  }
17517  //for List2
17518  LocFound = false;
17519  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
17520  {
17521  if((*ListPtr2) == Location)
17522  {
17523  LocPtr2 = ListPtr2; //may be modified later
17524  LocFound = true;
17525  }
17526  if(ListPtr2->SubString(1, 3) == "%%%")
17527  {
17528  AnsiString CDTTime = ListPtr2->SubString(4, 5);
17529  //now adjust the time to correspond to the repeat if there is one
17530  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
17531  {
17532  IncMinutes = -1;
17533  FirstServiceTime = TDateTime(-1);
17534  bool BreakFlag = false;
17535  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
17536  {
17537  if(TDVIt->ServiceReference == Ref2)
17538  {
17539  if(Ref2Target > Ref2Count)
17540  {
17541  Ref2Count++;
17542  continue;
17543  }
17544  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
17545  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
17546  {
17547  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
17548  {
17549  FirstServiceTime = AVIt->EventTime;
17550  BreakFlag = true;
17551  break;
17552  }
17553  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
17554  {
17555  FirstServiceTime = AVIt->ArrivalTime;
17556  BreakFlag = true;
17557  break;
17558  }
17559  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
17560  {
17561  FirstServiceTime = AVIt->DepartureTime;
17562  BreakFlag = true;
17563  break;
17564  }
17565  }
17566  if(BreakFlag)
17567  {
17568  break;
17569  }
17570  }
17571  }
17572  if(IncMinutes == -1)
17573  {
17574  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17575  }
17576  if(FirstServiceTime == TDateTime(-1))
17577  {
17578  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17579  }
17580  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
17581  }
17582  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
17583  {
17584  LocFound = false;
17585  continue;
17586  }
17587  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
17588  {
17589  break;
17590  }
17591  if(Time2 > CDTTime) //not there yet so go on
17592  {
17593  LocFound = false;
17594  continue;
17595  }
17596  if(Time2 < CDTTime) //gone too far so can stop now
17597  {
17598  break;
17599  }
17600  }
17601  }
17602  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
17603  {
17604  Utilities->CallLogPop(2229);
17605  return( false);
17606  }
17607  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
17608  //set ListPtr1 to the search start position
17609  if(Arrival)
17610  {
17611  LP1 = List1.begin();
17612  LP1--; //now points to before the first entry
17613  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
17614  {
17615  if(ListPtr1 == List1.begin())
17616  {
17617  break;
17618  }
17619  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
17620  {
17621  ListPtr1++; //point to one past the cdt
17622  break;
17623  }
17624  }
17625  //set ListPtr2 to the search start position
17626  LP2 = List2.begin();
17627  LP2--; //now points to before the first entry
17628  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
17629  {
17630  if(ListPtr2 == List2.begin())
17631  {
17632  break;
17633  }
17634  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
17635  {
17636  ListPtr2++; //point to one past the cdt
17637  break;
17638  }
17639  }
17640  //ListPtr1 & 2 now at search start position
17641  LP1 = ListPtr1;
17642  LP2 = ListPtr2;
17643  //now search forwards, i.e. for common locations before Location
17644  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
17645  {
17646  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
17647  {
17648  break;
17649  }
17650  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
17651  {
17652  break;
17653  }
17654  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
17655  {
17656  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
17657  {
17658  break;
17659  }
17660  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
17661  {
17662  break;
17663  }
17664  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
17665  {
17666  Utilities->CallLogPop(2230);
17667  return( true);
17668  }
17669  }
17670  }
17671  }
17672 
17673  //now, for the departure analysis, reset the start positions and search locations after Location
17674 
17675  else
17676  {
17677  LP1 = LocPtr1;
17678  LP1++; //start at one past the location itself
17679  LP2 = LocPtr2;
17680  LP2++;
17681  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
17682  {
17683  if(ListPtr1 == List1.end()) //reached end point so stop
17684  {
17685  break;
17686  }
17687  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
17688  {
17689  break;
17690  }
17691  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
17692  {
17693  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
17694  {
17695  break;
17696  }
17697  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
17698  {
17699  break;
17700  }
17701  if((*ListPtr1) == (*ListPtr2)) //found a common later location
17702  {
17703  Utilities->CallLogPop(2231);
17704  return( true);
17705  }
17706  }
17707  }
17708  }
17709  Utilities->CallLogPop(2232);
17710  return( false);
17711 }
17712 
17713 // ---------------------------------------------------------------------------
17714 
17715 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TExitList &ExitList, AnsiString &AllowedExits) const
17716 {
17717  // changed at v2.7.0 to show allowable exit elements
17718  if(ExitList.empty())
17719  {
17720  return("");
17721  }
17722  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
17723  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
17724  AnsiString ExitLocList = "";
17725  AllowedExits = "";
17726 
17727  unsigned int Counter = 0;
17728  for(TExitListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
17729  {
17730  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
17731  Counter++;
17732  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
17733  {
17734  ExitLocList += "\n";
17735  }
17736  }
17737  if(StartName == "")
17738  {
17739  if(ExitList.size() == 1)
17740  {
17741  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
17742  Utilities->CallLogPop(1571);
17743  return(" at " + ID);
17744  }
17745  else
17746  {
17747  Utilities->CallLogPop(1572);
17748  if(ExitList.size() < 4)
17749  {
17750  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17751  return("");
17752  }
17753  else
17754  {
17755  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17756  return("");
17757  }
17758  }
17759  }
17760  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
17761  {
17762  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
17763  {
17764  Utilities->CallLogPop(1570);
17765  if(ExitList.size() < 4)
17766  {
17767  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17768  return("");
17769  }
17770  else
17771  {
17772  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17773  return("");
17774  }
17775  }
17776  }
17777  Utilities->CallLogPop(1569);
17778  if(ExitList.size() < 4)
17779  {
17780  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17781  return(" at " + StartName);
17782  }
17783  else
17784  {
17785  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17786  return(" at " + StartName);
17787  }
17788 }
17789 
17790 // ---------------------------------------------------------------------------
17791 /* can't trust this as locations within a vector may not be contiguous
17792  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
17793  {
17794  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
17795  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
17796  //must be preceded by a TimeLoc departure
17797  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
17798  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
17799  {
17800  if((AVPtr + x) < TDEPtr->ActionVector.end())
17801  {
17802  AnsiString xx = (AVPtr + x)->Command;//test
17803  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
17804  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
17805  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
17806  {
17807  Utilities->CallLogPop();
17808  return false;
17809  }
17810  else if((AVPtr + x)->SequenceType == Finish)
17811  {
17812  Utilities->CallLogPop();
17813  return true;
17814  }
17815  }
17816  }
17817  Utilities->CallLogPop();
17818  return false;
17819  }
17820 */
17821 // ---------------------------------------------------------------------------
17822 
17823 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
17824 {
17825  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
17826  AnsiString FormatStr = "####0.0";
17827  AnsiString AvLateArrMins = "";
17828  AnsiString AvEarlyArrMins = "";
17829  AnsiString AvLatePassMins = "";
17830  AnsiString AvEarlyPassMins = "";
17831  AnsiString AvLateDepMins = "";
17832 
17833  if(LateArrivals > 0)
17834  {
17835  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
17836  }
17837  if(EarlyArrivals > 0)
17838  {
17839  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
17840  }
17841  if(LatePasses > 0)
17842  {
17843  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
17844  }
17845  if(EarlyPasses > 0)
17846  {
17847  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
17848  }
17849  if(LateDeps > 0)
17850  {
17851  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
17852  }
17853  PerfFile << '\n' << '\n' << "***************************************";
17854  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
17855 
17856  if(OnTimeArrivals != 1)
17857  {
17858  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
17859  }
17860  else
17861  {
17862  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
17863  }
17864  if(LateArrivals > 1)
17865  {
17866  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
17867  }
17868  else if(LateArrivals == 1)
17869  {
17870  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
17871  }
17872  else
17873  {
17874  PerfFile << LateArrivals << " late arrivals" << '\n';
17875  }
17876  if(EarlyArrivals > 1)
17877  {
17878  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
17879  }
17880  else if(EarlyArrivals == 1)
17881  {
17882  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
17883  }
17884  else
17885  {
17886  PerfFile << EarlyArrivals << " early arrivals" << '\n';
17887  }
17888  if(OnTimePasses != 1)
17889  {
17890  PerfFile << OnTimePasses << " on-time passes" << '\n';
17891  }
17892  else
17893  {
17894  PerfFile << OnTimePasses << " on-time pass" << '\n';
17895  }
17896  if(LatePasses > 1)
17897  {
17898  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
17899  }
17900  else if(LatePasses == 1)
17901  {
17902  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
17903  }
17904  else
17905  {
17906  PerfFile << LatePasses << " late passes" << '\n';
17907  }
17908  if(EarlyPasses > 1)
17909  {
17910  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
17911  }
17912  else if(EarlyPasses == 1)
17913  {
17914  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
17915  }
17916  else
17917  {
17918  PerfFile << EarlyPasses << " early passes" << '\n';
17919  }
17920  if(OnTimeDeps != 1)
17921  {
17922  PerfFile << OnTimeDeps << " on-time departures" << '\n';
17923  }
17924  else
17925  {
17926  PerfFile << OnTimeDeps << " on-time departure" << '\n';
17927  }
17928  if(LateDeps > 1)
17929  {
17930  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
17931  }
17932  else if(LateDeps == 1)
17933  {
17934  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
17935  }
17936  else
17937  {
17938  PerfFile << LateDeps << " late departures" << '\n';
17939  }
17940  TDateTime TempExcessLCDownTime;
17941  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
17942  {
17943 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
17944  //later perf summaries with lower values, changed at v2.8.0
17945 // {
17946  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
17947 // }
17948 /*
17949  else
17950  {
17951  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
17952  }
17953 */
17954  if(TempExcessLCDownTime > TDateTime(0))
17955  {
17956  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
17957  }
17958  }
17959 
17960  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
17961 
17962  if(ExcessLCDownMins > 0.1)
17963  {
17964  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
17965  }
17966  if(MissedStops != 1)
17967  {
17968  PerfFile << MissedStops << " missed stops" << '\n';
17969  }
17970  else
17971  {
17972  PerfFile << MissedStops << " missed stop" << '\n';
17973  }
17974  if(OtherMissedEvents != 1)
17975  {
17976  PerfFile << OtherMissedEvents << " other missed events" << '\n';
17977  }
17978  else
17979  {
17980  PerfFile << OtherMissedEvents << " other missed event" << '\n';
17981  }
17982  if(UnexpectedExits != 1)
17983  {
17984  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
17985  }
17986  else
17987  {
17988  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
17989  }
17990  if(IncorrectExits != 1)
17991  {
17992  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
17993  }
17994  else
17995  {
17996  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
17997  }
17998  if(NumFailures != 1)
17999  {
18000  PerfFile << NumFailures << " train failures" << '\n';
18001  }
18002  else
18003  {
18004  PerfFile << NumFailures << " train failure" << '\n';
18005  }
18006  if(AvHoursIntValue > 0)
18007  {
18008  if(AvHoursIntValue == 1)
18009  {
18010  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
18011  }
18012  else
18013  {
18014  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
18015  }
18016  }
18017  AnsiString AvLateMinsLocsNotReached = "";
18018 
18020  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
18021  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
18022 
18023  if(LocsNotReached > 0)
18024  {
18025  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
18026  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
18027  }
18028  if(SPADRisks != 1)
18029  {
18030  PerfFile << SPADRisks << " SPAD risks" << '\n';
18031  }
18032  else
18033  {
18034  PerfFile << SPADRisks << " SPAD risk" << '\n';
18035  }
18036  if(SPADEvents != 1)
18037  {
18038  PerfFile << SPADEvents << " SPADs" << '\n';
18039  }
18040  else
18041  {
18042  PerfFile << SPADEvents << " SPAD" << '\n';
18043  }
18044  if(Derailments != 1)
18045  {
18046  PerfFile << Derailments << " derailments" << '\n';
18047  }
18048  else
18049  {
18050  PerfFile << Derailments << " derailment" << '\n';
18051  }
18052  if(CrashedTrains != 1)
18053  {
18054  PerfFile << CrashedTrains << " crashed trains" << '\n';
18055  }
18056  else
18057  {
18058  PerfFile << CrashedTrains << " crashed train" << '\n';
18059  }
18060  PerfFile << '\n' << "***************************************" << '\n';
18061 
18062  bool DerailSPADFlag = false, CrashFlag = false;
18063 
18064  int OverallScorePercent = 100;
18065  int TotArrDep = 0;
18066  double TotLateMinsFactor = 1;
18067  double MissedStopAndSPADRiskFactor = 1;
18068  double NetNegFactor = 1;
18069 
18071  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
18072  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
18073  // 'no timetabled departures... message, which was inappropriate
18074 
18075  if((SPADEvents > 0) || (Derailments > 0))
18076  {
18077  OverallScorePercent = 5; // overrides other calculations
18078  DerailSPADFlag = true;
18079  }
18080  if(CrashedTrains > 0)
18081  {
18082  OverallScorePercent = 0; // overrides other calculations
18083  CrashFlag = true;
18084  }
18085  if(OverallScorePercent == 100)
18086  {
18087  if(TotArrDep > 0)
18088  {
18089  TotLateMinsFactor =
18091  ((OtherMissedEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDep);
18092  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
18093  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
18094  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDep);
18095  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
18096  // of arrivals & departures, where 4% = half, 8% = a quarter etc
18097  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
18098  // NetNegfactor: product of the above two
18099  OverallScorePercent = 100 * NetNegFactor;
18100  }
18101  }
18102  if((TotArrDep > 0) || DerailSPADFlag || CrashFlag)
18103  // flag condits added at v1.1.4 - see above for what the error was
18104  {
18105  AnsiString OneFailureString = ", though the failure would account for some poor performance";
18106  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
18107  AnsiString AddedString = "";
18108  if(NumFailures == 1)
18109  {
18110  AddedString = OneFailureString;
18111  }
18112  if(NumFailures > 1)
18113  {
18114  AddedString = TwoOrMoreFailureString;
18115  }
18116  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
18117  AnsiString Rating = "";
18118  if(OverallScorePercent == 100)
18119  {
18120  Rating = "Perfect!";
18121  }
18122  else if(OverallScorePercent >= 95)
18123  {
18124  Rating = "Excellent";
18125  }
18126  else if(OverallScorePercent >= 90)
18127  {
18128  Rating = "Very good";
18129  }
18130  else if(OverallScorePercent >= 80)
18131  {
18132  Rating = "Good";
18133  }
18134  else if(OverallScorePercent >= 70)
18135  {
18136  Rating = "Fair";
18137  }
18138  else if(OverallScorePercent >= 60)
18139  {
18140  Rating = "Unacceptable" + AddedString;
18141  }
18142  else if(OverallScorePercent >= 50)
18143  {
18144  Rating = "Poor" + AddedString;
18145  }
18146  else if(OverallScorePercent >= 40)
18147  {
18148  Rating = "Bad" + AddedString;
18149  }
18150  else if(OverallScorePercent >= 30)
18151  {
18152  Rating = "Very bad" + AddedString;
18153  }
18154  else if(OverallScorePercent >= 20)
18155  {
18156  Rating = "Terrible" + AddedString;
18157  }
18158  else if(OverallScorePercent >= 10)
18159  {
18160  Rating = "Appalling" + AddedString;
18161  }
18162  else if(OverallScorePercent >= 5)
18163  {
18164  if(DerailSPADFlag)
18165  {
18166  Rating = "Disastrous - potential loss of life";
18167  }
18168  // SPADs/Derailments
18169  else
18170  {
18171  Rating = "Dire" + AddedString;
18172  }
18173  }
18174  else if(OverallScorePercent < 5)
18175  {
18176  if(CrashFlag)
18177  {
18178  Rating = "Catastrophic - loss of life"; // Crashes
18179  }
18180  else
18181  {
18182  Rating = "Abysmal";
18183  }
18184  }
18185  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
18186  }
18187  else
18188  {
18189  PerfFile << "\nThere were no timetabled departures or arrivals so there is insufficient information to provide a performance score or rating" << '\n';
18190  }
18191  PerfFile << '\n' << "***************************************";
18192  Utilities->CallLogPop(1736);
18193 }
18194 
18195 // ---------------------------------------------------------------------------
18196 
18198 {
18199  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
18200  for(unsigned int x = 0; x < TrainVector.size(); x++)
18201  {
18202  TTrain &Train = TrainVectorAt(58, x);
18203  if(Train.Crashed)
18204  // can't use background colours for crashed & derailed because same colour
18205  {
18206  CrashWarning = true;
18207  }
18208  else if(Train.Derailed)
18209  // can't use background colours for crashed & derailed because same colour
18210  {
18211  DerailWarning = true;
18212  }
18213  else if(Train.BackgroundColour == clSPADBackground)
18214  // use colour as that changes as soon as passes signal
18215  {
18216  SPADWarning = true;
18217  }
18218  else if(Train.BackgroundColour == clTrainFailedBackground)
18219  {
18220  TrainFailedWarning = true;
18221  }
18222  else if(Train.BackgroundColour == clCallOnBackground)
18223  // use colour as also stopped at signal
18224  {
18225  CallOnWarning = true;
18226  }
18227  else if(Train.BackgroundColour == clSignalStopBackground)
18228  // use colour to distinguish from call-on
18229  {
18230  SignalStopWarning = true;
18231  }
18232  else if(Train.BackgroundColour == clBufferAttentionNeeded)
18233  // use colour to distinguish from ordinary buffer stop
18234  {
18235  BufferAttentionWarning = true;
18236  }
18237  }
18238  Utilities->CallLogPop(1796);
18239 }
18240 
18241 // ---------------------------------------------------------------------------
18242 
18244 {
18245  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
18246 
18247  // calculate lateness for running trains
18250  for(unsigned int x = 0; x < TrainVector.size(); x++)
18251  {
18252  TTrain &Train = TrainVectorAt(64, x);
18253  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
18254  AVEntryPtr++)
18255  {
18256  if(AVEntryPtr < Train.ActionVectorEntryPtr)
18257  {
18258  continue;
18259  }
18260  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.StoppedAtLocation && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
18261  TTClockTime))
18262  {
18263  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
18265  }
18266 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
18267  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
18268  TTClockTime))
18269  {
18270  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
18271  OperatingTrainArrDep++;
18272  }
18273 */
18274  }
18275  }
18276 
18277  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
18280 
18281  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
18282  {
18283  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
18284  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
18285  int IncrementalMinutes = 0;
18286  if(AVEntryLast.FormatType == Repeat)
18287  {
18288  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
18289  }
18290  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
18291  {
18292  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
18293  if(TTOD.RunningEntry != NotStarted)
18294  {
18295  continue;
18296  }
18297  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
18298  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
18299  bool TrainOperatingFlag = false;
18300  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
18301  {
18302  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
18303  {
18304  TrainOperatingFlag = true;
18305  break;
18306  }
18307  }
18308  if(TrainOperatingFlag)
18309  {
18310  continue;
18311  }
18312  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
18313  {
18314  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
18315  }
18316  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
18317  {
18318  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
18319  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
18320  {
18321  break; // all the rest will also be greater (& default of -1 will be less)
18322  }
18323  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
18324  {
18325  break; // all the rest will also be greater (& default of -1 will be less)
18326  }
18327  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
18328  {
18329  break; // all the rest will also be greater (& default of -1 will be less)
18330  }
18331  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
18332  {
18333  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
18335  }
18336 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
18337  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
18338  {
18339  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
18340  NotStartedTrainArrDep++;
18341  }
18342 */
18343  }
18344  }
18345  }
18346  Utilities->CallLogPop(1894);
18347 }
18348 
18349 // ---------------------------------------------------------------------------
18350 
18352 // new v2.2.0 for OperatorActionPanel
18353 // clears entries then adds values for running trains then for continuation entries
18354 // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
18355 {
18356  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
18357  OpTimeToActMultiMap.clear();
18358  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
18359 
18360  if(!TrainVector.empty())
18361  // build OpTimeToActMultiMap entries for running trains
18362  {
18363  AnsiString HeadCode;
18364  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
18365  int TrainID;
18366  THCandTrainPosParam HCandTrainPosParam;
18367  for(unsigned int x = 0; x < TrainVector.size(); x++)
18368  {
18369  HeadCode = TrainVectorAt(62, x).HeadCode;
18370  TrainID = TrainVectorAt(63, x).TrainID;
18371  HCandTrainPosParam.first = HeadCode;
18372  HCandTrainPosParam.second = TrainID;
18373  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
18374  if((TimeToAct >= 0) && (TimeToAct < 59.9))
18375  // -1 indicates don't display
18376  {
18377  OpTimeToActMultiMapEntry.first = TimeToAct;
18378  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
18379  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
18380  }
18381  }
18382  }
18383 /*
18384  * class TContinuationTrainExpectationEntry
18385  {
18386  public:
18387  AnsiString Description; ///< service description
18388  AnsiString HeadCode; ///< service headcode
18389  int RepeatNumber; ///< service RepeatNumber
18390  int IncrementalMinutes; ///< Repeat separation in minutes
18391  int IncrementalDigits; ///< Repeat headcode separation
18392  int VectorPosition; ///< TrackVectorPosition for the continuation element
18393  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
18394  };
18395 
18396  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
18397  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
18398  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
18399  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
18400 */
18401 
18403  // build OpTimeToActMultiMap entries for expected trains
18404  {
18405  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
18406  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
18407  float TimeToAct = 0; // minutes
18408  int DistanceToRedSignal = 0; // metres
18409  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
18410  // used to ensure only one train displayed for a given continuation
18411  ContinuationEntryVecPosVector.clear();
18412  bool LaterTrain = false;
18415  {
18416  LaterTrain = false;
18417  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
18418  {
18419  CTEIt++;
18420  continue; // not interested in running or exited trains
18421  }
18422  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
18423  {
18424  CTEIt++;
18425  continue;
18426  // don't include trains not entered yet when a train is already on the continuation
18427  }
18428  if(!ContinuationEntryVecPosVector.empty())
18429  {
18430  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
18431  {
18432  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
18433  {
18434  LaterTrain = true;
18435  ;
18436  // skip past remaining trains waiting to enter at same point
18437  break;
18438  }
18439  }
18440  }
18441  if(LaterTrain)
18442  {
18443  CTEIt++;
18444  continue;
18445  }
18446  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
18447  AnsiString HeadCode = CTEIt->second.HeadCode;
18448  float CurrentStopTime; // set to 0 at start of function
18449  float LaterStopTime; // set to 0 at start of function
18450  float RecoverableTime; // set to 0 at start of function
18451  int AvTrackSpeed; // set to 0 at start of function
18452  int TrainID = -1; // not yet allocated for train still to enter
18453  bool SigControlAndCanPassRedSignal = false;
18454  // doesn't apply for a continuation
18455  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
18456  // EntryPos always 0 for entering at a continuation
18457  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(1),
18458  // at(1) to skip past the Start train value
18459  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
18460  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
18461  // for a train it's the one in front of LeadElement
18462  if(AvTrackSpeed < 30)
18463  {
18464  AvTrackSpeed = 30;
18465  }
18466  if(DistanceToRedSignal == -1)
18467  {
18468  TimeToAct = 60.0;
18469  }
18470  else
18471  {
18472  int Speed = AvTrackSpeed;
18473  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
18474  if(AvTrackSpeed > MaxSpeed)
18475  {
18476  Speed = MaxSpeed;
18477  }
18478  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(1).SignallerControl)
18479  // defined in timetable as under signaller control
18480  {
18481  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
18482  LaterStopTime = 0;
18483  }
18484  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
18485  // accel & decel taken into account in
18486  // CalcDistanceToRedSignalandStopTime
18487  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
18488  // don't need CurrentStopTime or RecoverableTime for continuation entries
18489  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
18490  TimeToAct += MinsBefEnter;
18491  }
18492  THCandTrainPosParam HCandTrainPosParam;
18493  HCandTrainPosParam.first = HeadCode;
18494  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
18495  // -1-CTE... because 2nd value covers TrainID if +ve &
18496  // continuation track vector position if -ve, -1 allows for vecpos being 0
18497  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
18498  {
18499  OpTimeToActMultiMapEntry.first = TimeToAct;
18500  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
18501  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
18502  }
18503  CTEIt++;
18504  }
18505  }
18506  Utilities->CallLogPop(2081);
18507 }
18508 
18509 // ---------------------------------------------------------------------------
18510 
18511 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
18512  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
18513  float &RecoverableTime, int &AvTrackSpeed)
18514 // new v2.2.0
18515 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
18516 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
18517 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
18518 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
18519 // aren't used - this means there is no display for the train in question
18520 {
18521  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
18522  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
18523  int DistanceToRedSignal = 0;
18524  int CumTrackSpeed = 0;
18525  // average track speed, in case need to use in time calc
18526  int TrackSpeedCount = 0;
18527 
18528  //below added at v2.6.1
18529  if(TrainID > -1)
18530  {
18531  TTrain &Train = TrainVectorAtIdent(51, TrainID);
18532  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
18533  Train.StationStopCalculated = false;
18534  }
18535  AvTrackSpeed = 0;
18536  int CurrentElement = TrackVectorPosition;
18537  int CurrentEntryPos = TrackVectorPositionEntryPos;
18538  int NextElement;
18539  int NextEntryPos;
18540  int NextExitPos;
18541 
18542  CurrentStopTime = 0;
18543  LaterStopTime = 0;
18544  RecoverableTime = 0;
18545  if(CurrentElement == -1) // end element, no action needed
18546  {
18547  Utilities->CallLogPop(2094);
18548  return(-1);
18549  }
18550  int CurrentExitPos;
18551 
18552  // get ExitPos for first element to be measured
18553  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
18554  {
18555  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
18556  {
18557  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
18558  {
18559  CurrentExitPos = 1;
18560  }
18561  else
18562  {
18563  CurrentExitPos = 3;
18564  }
18565  }
18566  else
18567  {
18568  CurrentExitPos = 0; // trailing point
18569  }
18570  }
18571  else
18572  {
18573  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
18574  }
18575  // get CumTrackSpeed for first measured element
18576 
18577  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
18578  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
18579 
18580  // check if currently stopped at a location, and if so add the remaining dwell time
18581  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
18582  if(TrainID > -1)
18583  // -1 for a continuation and can't be at a location as not yet entered
18584  {
18585  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
18587  // this used to deduct from RecoverableTime when arrive at a location
18588  if(Train.StoppedAtLocation)
18589  {
18590  if(Train.StoppedForTrainInFront)
18591  {
18592  Utilities->CallLogPop(2082);
18593  return(-1); // no action needed
18594  }
18595  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
18596  {
18597  Utilities->CallLogPop(2083);
18598  return(-1); // not due a departure so no action needed
18599  }
18600  else // due a departure
18601  {
18602  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
18603  // can't convert a TDateTime to a float directly
18604  CurrentStopTime = float(TimeToDepart);
18605  AVPtr++;
18606  }
18607  }
18608  }
18609  // check if CurrentElement is a red signal, but ok if autosig route after
18610  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
18611  // ok if autosig route after red signal
18612  {
18613  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
18614  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
18615  int RouteNumber; // holder for referenced value, not used
18616  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
18617  {
18618  Utilities->CallLogPop(2078);
18619  return(-1);
18620  }
18621  else if(SigControlAndCanPassRedSignal)
18622  // ignore signal and increment CurrentElement to NextElement
18623  {
18624  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
18625  {
18626  if((NextEntryPos == 0) || (NextEntryPos == 2))
18627  // leading entry point
18628  {
18629  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
18630  {
18631  NextExitPos = 1;
18632  }
18633  else
18634  {
18635  NextExitPos = 3;
18636  }
18637  }
18638  else
18639  {
18640  NextExitPos = 0; // trailing entry point
18641  }
18642  }
18643  else
18644  {
18645  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
18646  }
18647  CurrentElement = NextElement;
18648  CurrentEntryPos = NextEntryPos;
18649  CurrentExitPos = NextExitPos;
18650  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
18651  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
18652  }
18653  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
18654  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
18655  {
18656  Utilities->CallLogPop(2084);
18657  return(0);
18658  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
18659  }
18660  }
18661  int LaterStopNumber = 0;
18662  int x = 0;
18663  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
18664 
18665  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
18666  // not red signal next (in fwd direction) so enter loop to calc CumLength
18667  {
18668  x++; // added in v2.4.0 as above
18669  if(x > 5000)
18670  {
18671  Utilities->CallLogPop(2120);
18672  return(-1);
18673  }
18674  if(CurrentEntryPos > 1)
18675  {
18676  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
18677  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
18678  }
18679  else
18680  {
18681  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
18682  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
18683  }
18684  TrackSpeedCount++;
18685 
18686  // added at v2.6.1 to find DistanceToStationStop for trains running early
18687  if(TrainID > -1) //can ignore continuation entries as these don't run early
18688  {
18689  TTrain &Train = TrainVectorAtIdent(52, TrainID);
18690  if(!Train.StationStopCalculated)
18691  {
18692  if(Train.TrainMode == Timetable)
18693  {
18694  bool StopRequired = false;
18695  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
18696  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
18697  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
18698  {
18699  // no need to add in the length of element to CumulativeLength
18700  if(StopRequired)
18701  {
18702  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
18703  Train.StationStopCalculated = true; //don't want to update it with later stops
18704  }
18705  }
18706  }
18707  }
18708  }
18709  // check for train in front, but if on a bridge on other track then ok
18710  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
18711  int TrainOnElement;
18712  if(TE.TrackType != Bridge)
18713  {
18714  TrainOnElement = TE.TrainIDOnElement;
18715  }
18716  else
18717  {
18718  if(CurrentEntryPos > 1)
18719  {
18720  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
18721  }
18722  else
18723  {
18724  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
18725  }
18726  }
18727  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
18728  // train in front before red signal
18729  {
18730  Utilities->CallLogPop(2085);
18731  return(-1);
18732  }
18733  // add to stoptime if required
18734  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
18735  {
18736  double StopTimeDouble;
18737  while(AVPtr->FormatType == PassTime)
18738  {
18739  AVPtr++; // skip past any passes
18740  }
18741  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
18742  (AVPtr->FormatType == TimeTimeLoc)))
18743  // stop due here so calc dwell time & advance Ptr
18744  {
18745  if(AVPtr->FormatType == TimeTimeLoc)
18746  {
18747  LaterStopNumber++;
18748  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
18749  if(StopTimeDouble < 0.5)
18750  {
18751  StopTimeDouble = 0.5;
18752  }
18753  // at least 30 secs delay at station
18754  // can't convert a TDateTime to a float directly
18755  LaterStopTime += float(StopTimeDouble);
18756  RecoverableTime += StopTimeDouble - 0.5;
18757  if((LaterStopNumber == 1) && (TrainID > -1))
18758  {
18759  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
18760  }
18761  AVPtr++;
18762  }
18763  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
18764  {
18765  if((AVPtr + 1)->FormatType == TimeLoc)
18766  // must be a departure
18767  {
18768  LaterStopNumber++;
18769  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
18770  // can't convert a TDateTime to a float directly
18771  if(StopTimeDouble < 0.5)
18772  {
18773  StopTimeDouble = 0.5;
18774  }
18775  // at least 30 secs delay at station
18776  LaterStopTime += float(StopTimeDouble);
18777  RecoverableTime += StopTimeDouble - 0.5;
18778  if((LaterStopNumber == 1) && (TrainID > -1))
18779  {
18780  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
18781  }
18782  AVPtr++;
18783  AVPtr++;
18784  }
18785  else // not a departure, does something else at the location so no calculation needed
18786  {
18787  Utilities->CallLogPop(2086);
18788  return(-1);
18789  }
18790  }
18791  }
18792  }
18793  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
18794  if(NextElement == -1) // reached end element, no action needed
18795  {
18796  Utilities->CallLogPop(2077);
18797  return(-1);
18798  }
18799  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
18800  // get NextExitPos
18801  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
18802  {
18803  if((NextEntryPos == 0) || (NextEntryPos == 2))
18804  // leading entry point
18805  {
18806  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
18807  {
18808  NextExitPos = 1;
18809  }
18810  else
18811  {
18812  NextExitPos = 3;
18813  }
18814  }
18815  else
18816  {
18817  NextExitPos = 0; // trailing entry point
18818  }
18819  }
18820  else
18821  {
18822  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
18823  }
18824  CurrentElement = NextElement;
18825  CurrentEntryPos = NextEntryPos;
18826  CurrentExitPos = NextExitPos;
18827  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
18828  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
18829  }
18830  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
18831  // ok if autosig route after red signal, no action needed
18832  {
18833  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
18834  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
18835  int RouteNumber; // holder for referenced value, not used
18836  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
18837  {
18838  Utilities->CallLogPop(2095);
18839  return(-1);
18840  }
18841  }
18842  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
18843  // average line speed/2 (for half distance accelerating and half decelerating.
18844 
18845  float MaxAllowableSpeed;
18846 
18847  if(TrackSpeedCount > 0)
18848  {
18849  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
18850  }
18851  else // shouldn't reach here but include to prevent divide by zero error
18852  {
18853  if(CurrentEntryPos > 1)
18854  {
18855  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
18856  }
18857  else
18858  {
18859  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
18860  }
18861  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
18862  }
18863  float KmPerLocationStop;
18864 
18865  if(LaterStopNumber > 0)
18866  {
18867  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
18868  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
18869  }
18870  else
18871  {
18872  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
18873  // using linear trendline for accel & decel distance at various speeds
18874  // at half braking, speed never < 60 using this
18875  }
18876  if(AvTrackSpeed > MaxAllowableSpeed)
18877  {
18878  AvTrackSpeed = MaxAllowableSpeed;
18879  }
18880  Utilities->CallLogPop(2096);
18881  return(DistanceToRedSignal);
18882 }
18883 
18884 // ---------------------------------------------------------------------------
18885 // end of TTrainController entries
18886 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:8320
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17224
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:113
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:347
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:50
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:14063
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:314
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:313
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:443
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:646
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:62
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1599
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:884
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:151
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:14086
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:13749
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:335
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:18351
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:754
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:15101
TTrain::CheckNewServiceDepartureTime
AnsiString CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time
Definition: TrainUnit.cpp:6950
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:94
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:51
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:980
Create
@ Create
Definition: TrainUnit.h:50
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:733
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:18230
Arrive
@ Arrive
Definition: TrainUnit.h:50
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:50
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:312
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:353
Depart
@ Depart
Definition: TrainUnit.h:50
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:702
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:10972
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:700
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:393
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:700
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:297
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7034
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1516
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:51
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:890
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1528
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:636
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:119
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:684
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1540
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:68
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:64
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:460
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:694
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6108
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:65
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:397
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:69
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:18978
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:13033
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:153
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:335
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:966
TTrain::TrainFailed
bool TrainFailed
added at v2.4.0 to indicate failure
Definition: TrainUnit.h:379
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:949
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:740
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:308
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:945
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7196
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:355
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:434
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:335
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6222
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:740
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:9372
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:315
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:881
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:668
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:14832
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:793
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:972
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:619
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:979
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:184
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:18020
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:740
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:10925
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1520
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:8292
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:113
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:932
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:690
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:14847
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:242
Intermediate
@ Intermediate
Definition: TrainUnit.h:75
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:930
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6037
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:946
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3433
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:628
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:64
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:456
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:418
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:9334
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:52
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:449
NotStarted
@ NotStarted
Definition: TrainUnit.h:86
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10541
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:787
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:736
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:315
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:705
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:343
TTrainDataEntry
Contains all data for a single train.
Definition: TrainUnit.h:184
LeadMid
@ LeadMid
Definition: TrainUnit.h:275
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:780
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:18493
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:13829
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:944
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6243
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:686
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:986
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:229
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:982
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:192
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:797
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:760
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:676
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:8589
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:14950
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:275
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:221
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:833
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:15809
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:14888
StartNew
@ StartNew
Definition: TrainUnit.h:64
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:50
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:275
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:704
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:316
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:51
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:408
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:638
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:17768
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:10533
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:955
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5646
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:463
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:163
TTrain
Definition: TrainUnit.h:279
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:952
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:10552
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:155
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11171
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:828
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:227
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:810
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:440
GapJump
@ GapJump
Definition: TrackUnit.h:67
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:973
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:782
NoSequence
@ NoSequence
Definition: TrainUnit.h:75
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:151
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:151
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:375
Finish
@ Finish
Definition: TrainUnit.h:75
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:5988
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:8970
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:335
TTrackElement::TempTrackMarker23
bool TempTrackMarker23
Utility markers for program use.
Definition: TrackUnit.h:141
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1567
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:335
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:329
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:202
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:649
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:333
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:14929
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:777
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:298
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:174
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:731
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:422
TExitListIterator
TExitList::iterator TExitListIterator
Definition: TrainUnit.h:95
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:664
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:14652
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1287
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:5878
End
@ End
Definition: TrackUnit.h:77
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:365
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6824
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8518
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:310
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackElement.at(At)
Definition: TrackUnit.cpp:10270
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:65
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:303
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:371
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:15012
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:127
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:940
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:581
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:990
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:786
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:186
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:387
SignalPost
@ SignalPost
Definition: TrackUnit.h:67
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:934
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled action, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:426
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:143
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1544
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:318
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:962
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:186
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5317
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:11596
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:127
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:788
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:717
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:837
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:385
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:76
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:436
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:66
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:785
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:754
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:879
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1859
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:440
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:783
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:950
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:64
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:96
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:113
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:440
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:10605
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:928
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:416
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:953
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:9870
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:688
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:335
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:414
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:79
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:978
Pass
@ Pass
Definition: TrainUnit.h:52
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:740
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:702
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:70
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:2962
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:10989
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:770
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:69
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:965
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:447
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:720
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:818
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:7350
TExitList
std::list< int > TExitList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:91
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:938
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:662
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:323
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:292
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:971
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:766
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:8304
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:974
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:188
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:64
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:198
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6680
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:757
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:16944
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:428
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:14331
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10407
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:600
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:286
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3170
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:985
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:424
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:4928
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2483
RearSplit
@ RearSplit
Definition: TrainUnit.h:50
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:111
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:52
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:721
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:612
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:935
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:764
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:958
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:137
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10305
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2894
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6652
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:14429
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:52
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:787
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:13133
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:164
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
new v2.2.0, incremented in Interface.cpp, controls updating for OpTimeToActPanel
Definition: TrainUnit.h:802
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:349
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3383
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:931
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:300
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8114
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:7393
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2886
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:769
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:443
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:420
NewService
@ NewService
Definition: TrainUnit.h:50
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:15165
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1383
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:440
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:351
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1527
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:341
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:200
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:196
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:775
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:169
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:975
Crossover
@ Crossover
Definition: TrackUnit.h:67
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4538
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:682
AtLocation
@ AtLocation
Definition: TrainUnit.h:70
Signal
@ Signal
Definition: TrackUnit.h:77
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:937
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:968
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:693
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:155
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17461
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:816
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:17355
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1528
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:422
Exited
@ Exited
Definition: TrainUnit.h:86
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackElement.at(At)
Definition: TrackUnit.cpp:10257
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:14863
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7680
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:17287
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:987
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2100
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:440
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:57
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1549
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:927
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:440
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:700
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:288
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:742
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:50
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:395
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:754
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:957
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:933
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:335
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:64
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:969
Leave
@ Leave
Definition: TrainUnit.h:50
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:790
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:712
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:738
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:412
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:363
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2867
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:936
NoFormat
@ NoFormat
Definition: TrainUnit.h:64
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:750
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:64
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2412
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:440
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3412
Enter
@ Enter
Definition: TrainUnit.h:50
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:514
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7565
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:943
TTrain::NewTrainService
void NewTrainService(int Caller)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6182
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:14912
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:406
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:253
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:13663
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:692
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:8624
Timetable
@ Timetable
Definition: TrainUnit.h:58
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:14017
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6735
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:748
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:10840
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:684
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:109
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:17130
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:740
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:285
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:18243
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:521
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:80
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:401
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:710
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:240
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:746
Terminate
@ Terminate
Definition: TrainUnit.h:50
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:754
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
map of active track element names
Definition: TrackUnit.h:696
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:440
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:149
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:454
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:338
Running
@ Running
Definition: TrainUnit.h:86
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:262
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3509
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:880
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13005
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:14441
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed)
new v2.2.0, calcs distance to red signal, returns -1 for no signal found, for autosigs route after ne...
Definition: TrainUnit.cpp:18511
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:15188
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:414
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11060
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:634
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:215
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:791
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:389
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2297
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:18145
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:806
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1514
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:9056
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:8671
TTrainController::ControllerCheckNewServiceDepartureTime
AnsiString ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::CheckNewServiceDepartureTime for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:9563
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:960
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:130
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:516
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:8682
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:977
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1601
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:754
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3366
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3066
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:422
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:704
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:11013
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:70
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:52
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1512
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:8261
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:2930
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:101
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:15034
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:243
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:954
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:14408
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:147
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:808
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:441
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4494
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:255
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TExitList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:17715
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:9388
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:51
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:325
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:373
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:11096
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:99
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:8696
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:155
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:886
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:404
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:731
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:159
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1582
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:190
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:132
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:959
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:204
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:149
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:762
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:803
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:534
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:4951
TActionVectorEntry::ExitList
TExitList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:115
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:424
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1518
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1528
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:117
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:304
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:983
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:655
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6482
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:653
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:9040
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:878
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:776
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:321
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:15201
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:671
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:948
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:153
Points
@ Points
Definition: TrackUnit.h:67
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:724
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:361
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:771
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:13562
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6523
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:359
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:754
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:789
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:52
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3357
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:967
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:367
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:458
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:16405
Continuation
@ Continuation
Definition: TrackUnit.h:67
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:740
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7323
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:669
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:784
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:432
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:9742
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:331
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:17823
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3024
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:882
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:52
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:723
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:885
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:981
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:9419
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:89
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:438
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:622
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:648
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6425
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:445
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:279
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:179
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:465
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:52
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:964
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:18808
TTrackElement::TempTrackMarker01
bool TempTrackMarker01
Definition: TrackUnit.h:141
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:797
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:956
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:10698
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:290
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:261
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:628
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:664
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8224
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:63
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:740
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:295
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:709
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:164
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:707
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:951
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:17918
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:17874
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:744
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:103
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:259
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:75
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:889
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:302
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:49
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:664
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:335
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:779
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:988
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:80
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:9010
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:14355
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:6999
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:17144
TTrain::AValue
double AValue
this is a useful shorthand value in calculating speeds and transit times in SetTrainMovementValues [=...
Definition: TrainUnit.h:381
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5369
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:17113
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:9025
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:125
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13012
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:311
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:8368
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:14994
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:162
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:154
NoLocation
@ NoLocation
Definition: TrainUnit.h:70
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:899
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:357
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:194
NoMode
@ NoMode
Definition: TrainUnit.h:58
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:781
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:11444
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:102
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:713
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:754
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:64
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:153
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:214
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:799
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:774
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:752
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:531
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5035
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:440
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:9407
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:947
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:143
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:274
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:725
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable (the object is a member of TTrainController)
Definition: TrainUnit.h:217
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:14051
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7118
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:410
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:651
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1522
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:121
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:327
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:715
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:941
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:984
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:711
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:2988
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled action entries, null on creation
Definition: TrainUnit.h:103
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:123
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:377
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:976
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:103
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4661
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:684
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6585
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:151
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:369
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:795
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:754
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:939
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6142
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:709
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:64
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:51
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:714
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:18197
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:680
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:64
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:768
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:970
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:812
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:666
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:105
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:4940
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:399
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:151
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:670
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:306
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:659
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:713
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:65
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:778
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2912
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:91
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:626
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1037
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:961
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:17397
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6453
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3408
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:107
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2500
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:189
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:186
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6766
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:309
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:77
Repeat
@ Repeat
Definition: TrainUnit.h:65
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:929
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:13021
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:596
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:754
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:160
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:68
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:149
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:441
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:963
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:653
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:823
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:225
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5679
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:17139
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:145
Signaller
@ Signaller
Definition: TrainUnit.h:58
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:451
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:887
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:160
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:876
Bridge
@ Bridge
Definition: TrackUnit.h:67
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:942
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:257
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:335
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:74
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:391
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:75
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:524
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:67
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:111
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:103